~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/rtc/rtc-nuc900.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
47
struct nuc900_rtc {
 
48
        int                     irq_num;
 
49
        void __iomem            *rtc_reg;
 
50
        struct rtc_device       *rtcdev;
 
51
};
 
52
 
 
53
struct 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
 
 
62
static 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
 
 
86
static 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
 
 
105
static 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
 
 
119
static 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
 
 
137
static 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
 
 
151
static 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
 
 
162
static 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
 
 
184
static 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
 
 
195
static 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
 
 
217
static 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
 
 
225
static 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
                                IRQF_DISABLED, "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
 
 
280
fail4:  rtc_device_unregister(nuc900_rtc->rtcdev);
 
281
fail3:  iounmap(nuc900_rtc->rtc_reg);
 
282
fail2:  release_mem_region(res->start, resource_size(res));
 
283
fail1:  kfree(nuc900_rtc);
 
284
        return err;
 
285
}
 
286
 
 
287
static 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
 
 
306
static 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
 
 
314
static int __init nuc900_rtc_init(void)
 
315
{
 
316
        return platform_driver_probe(&nuc900_rtc_driver, nuc900_rtc_probe);
 
317
}
 
318
 
 
319
static void __exit nuc900_rtc_exit(void)
 
320
{
 
321
        platform_driver_unregister(&nuc900_rtc_driver);
 
322
}
 
323
 
 
324
module_init(nuc900_rtc_init);
 
325
module_exit(nuc900_rtc_exit);
 
326
 
 
327
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
 
328
MODULE_DESCRIPTION("nuc910/nuc920 RTC driver");
 
329
MODULE_LICENSE("GPL");
 
330
MODULE_ALIAS("platform:nuc900-rtc");