Blame view
drivers/rtc/rtc-vr41xx.c
9.3 KB
1da177e4c Linux-2.6.12-rc2 |
1 |
/* |
8417eb7a1 [PATCH] RTC subsy... |
2 |
* Driver for NEC VR4100 series Real Time Clock unit. |
1da177e4c Linux-2.6.12-rc2 |
3 |
* |
ada8e9514 Update Yoichi Yua... |
4 |
* Copyright (C) 2003-2008 Yoichi Yuasa <yuasa@linux-mips.org> |
1da177e4c Linux-2.6.12-rc2 |
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
bd0765098 [MIPS] separate p... |
20 |
#include <linux/err.h> |
1da177e4c Linux-2.6.12-rc2 |
21 22 23 |
#include <linux/fs.h> #include <linux/init.h> #include <linux/ioport.h> |
bd0765098 [MIPS] separate p... |
24 |
#include <linux/interrupt.h> |
1da177e4c Linux-2.6.12-rc2 |
25 |
#include <linux/module.h> |
8417eb7a1 [PATCH] RTC subsy... |
26 |
#include <linux/platform_device.h> |
1da177e4c Linux-2.6.12-rc2 |
27 28 29 |
#include <linux/rtc.h> #include <linux/spinlock.h> #include <linux/types.h> |
5d2a50371 rtc: move power o... |
30 |
#include <linux/log2.h> |
1da177e4c Linux-2.6.12-rc2 |
31 32 33 |
#include <asm/div64.h> #include <asm/io.h> |
1da177e4c Linux-2.6.12-rc2 |
34 |
#include <asm/uaccess.h> |
1da177e4c Linux-2.6.12-rc2 |
35 |
|
ada8e9514 Update Yoichi Yua... |
36 |
MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>"); |
1da177e4c Linux-2.6.12-rc2 |
37 |
MODULE_DESCRIPTION("NEC VR4100 series RTC driver"); |
4cad4431f rtc-vr41xx: add i... |
38 |
MODULE_LICENSE("GPL v2"); |
1da177e4c Linux-2.6.12-rc2 |
39 |
|
1da177e4c Linux-2.6.12-rc2 |
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
/* RTC 1 registers */ #define ETIMELREG 0x00 #define ETIMEMREG 0x02 #define ETIMEHREG 0x04 /* RFU */ #define ECMPLREG 0x08 #define ECMPMREG 0x0a #define ECMPHREG 0x0c /* RFU */ #define RTCL1LREG 0x10 #define RTCL1HREG 0x12 #define RTCL1CNTLREG 0x14 #define RTCL1CNTHREG 0x16 #define RTCL2LREG 0x18 #define RTCL2HREG 0x1a #define RTCL2CNTLREG 0x1c #define RTCL2CNTHREG 0x1e /* RTC 2 registers */ #define TCLKLREG 0x00 #define TCLKHREG 0x02 #define TCLKCNTLREG 0x04 #define TCLKCNTHREG 0x06 /* RFU */ #define RTCINTREG 0x1e #define TCLOCK_INT 0x08 #define RTCLONG2_INT 0x04 #define RTCLONG1_INT 0x02 #define ELAPSEDTIME_INT 0x01 #define RTC_FREQUENCY 32768 #define MAX_PERIODIC_RATE 6553 |
1da177e4c Linux-2.6.12-rc2 |
72 73 74 75 76 77 78 79 80 81 82 |
static void __iomem *rtc1_base; static void __iomem *rtc2_base; #define rtc1_read(offset) readw(rtc1_base + (offset)) #define rtc1_write(offset, value) writew((value), rtc1_base + (offset)) #define rtc2_read(offset) readw(rtc2_base + (offset)) #define rtc2_write(offset, value) writew((value), rtc2_base + (offset)) static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */ |
34af946a2 [PATCH] spin/rwlo... |
83 |
static DEFINE_SPINLOCK(rtc_lock); |
1da177e4c Linux-2.6.12-rc2 |
84 |
static char rtc_name[] = "RTC"; |
1da177e4c Linux-2.6.12-rc2 |
85 |
static unsigned long periodic_count; |
9b5ef64a3 rtc: update vr41x... |
86 |
static unsigned int alarm_enabled; |
2fac6674d rtc: bunch of dri... |
87 88 |
static int aie_irq; static int pie_irq; |
1da177e4c Linux-2.6.12-rc2 |
89 |
|
1da177e4c Linux-2.6.12-rc2 |
90 91 |
static inline unsigned long read_elapsed_second(void) { |
8417eb7a1 [PATCH] RTC subsy... |
92 |
|
1da177e4c Linux-2.6.12-rc2 |
93 |
unsigned long first_low, first_mid, first_high; |
8417eb7a1 [PATCH] RTC subsy... |
94 |
|
1da177e4c Linux-2.6.12-rc2 |
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
unsigned long second_low, second_mid, second_high; do { first_low = rtc1_read(ETIMELREG); first_mid = rtc1_read(ETIMEMREG); first_high = rtc1_read(ETIMEHREG); second_low = rtc1_read(ETIMELREG); second_mid = rtc1_read(ETIMEMREG); second_high = rtc1_read(ETIMEHREG); } while (first_low != second_low || first_mid != second_mid || first_high != second_high); return (first_high << 17) | (first_mid << 1) | (first_low >> 15); } static inline void write_elapsed_second(unsigned long sec) { spin_lock_irq(&rtc_lock); rtc1_write(ETIMELREG, (uint16_t)(sec << 15)); rtc1_write(ETIMEMREG, (uint16_t)(sec >> 1)); rtc1_write(ETIMEHREG, (uint16_t)(sec >> 17)); spin_unlock_irq(&rtc_lock); } |
8417eb7a1 [PATCH] RTC subsy... |
120 |
static void vr41xx_rtc_release(struct device *dev) |
1da177e4c Linux-2.6.12-rc2 |
121 |
{ |
1da177e4c Linux-2.6.12-rc2 |
122 123 |
spin_lock_irq(&rtc_lock); |
8417eb7a1 [PATCH] RTC subsy... |
124 125 126 127 128 |
rtc1_write(ECMPLREG, 0); rtc1_write(ECMPMREG, 0); rtc1_write(ECMPHREG, 0); rtc1_write(RTCL1LREG, 0); rtc1_write(RTCL1HREG, 0); |
1da177e4c Linux-2.6.12-rc2 |
129 130 |
spin_unlock_irq(&rtc_lock); |
bd0765098 [MIPS] separate p... |
131 132 |
disable_irq(aie_irq); disable_irq(pie_irq); |
1da177e4c Linux-2.6.12-rc2 |
133 |
} |
8417eb7a1 [PATCH] RTC subsy... |
134 |
static int vr41xx_rtc_read_time(struct device *dev, struct rtc_time *time) |
1da177e4c Linux-2.6.12-rc2 |
135 136 137 138 139 |
{ unsigned long epoch_sec, elapsed_sec; epoch_sec = mktime(epoch, 1, 1, 0, 0, 0); elapsed_sec = read_elapsed_second(); |
8417eb7a1 [PATCH] RTC subsy... |
140 141 142 |
rtc_time_to_tm(epoch_sec + elapsed_sec, time); return 0; |
1da177e4c Linux-2.6.12-rc2 |
143 |
} |
8417eb7a1 [PATCH] RTC subsy... |
144 |
static int vr41xx_rtc_set_time(struct device *dev, struct rtc_time *time) |
1da177e4c Linux-2.6.12-rc2 |
145 146 147 148 149 150 151 152 |
{ unsigned long epoch_sec, current_sec; epoch_sec = mktime(epoch, 1, 1, 0, 0, 0); current_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec); write_elapsed_second(current_sec - epoch_sec); |
8417eb7a1 [PATCH] RTC subsy... |
153 154 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
155 |
} |
8417eb7a1 [PATCH] RTC subsy... |
156 |
static int vr41xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) |
1da177e4c Linux-2.6.12-rc2 |
157 |
{ |
8417eb7a1 [PATCH] RTC subsy... |
158 159 |
unsigned long low, mid, high; struct rtc_time *time = &wkalrm->time; |
1da177e4c Linux-2.6.12-rc2 |
160 |
|
8417eb7a1 [PATCH] RTC subsy... |
161 |
spin_lock_irq(&rtc_lock); |
1da177e4c Linux-2.6.12-rc2 |
162 |
|
8417eb7a1 [PATCH] RTC subsy... |
163 164 165 |
low = rtc1_read(ECMPLREG); mid = rtc1_read(ECMPMREG); high = rtc1_read(ECMPHREG); |
9b5ef64a3 rtc: update vr41x... |
166 |
wkalrm->enabled = alarm_enabled; |
1da177e4c Linux-2.6.12-rc2 |
167 |
|
8417eb7a1 [PATCH] RTC subsy... |
168 |
spin_unlock_irq(&rtc_lock); |
1da177e4c Linux-2.6.12-rc2 |
169 |
|
8417eb7a1 [PATCH] RTC subsy... |
170 |
rtc_time_to_tm((high << 17) | (mid << 1) | (low >> 15), time); |
1da177e4c Linux-2.6.12-rc2 |
171 |
|
8417eb7a1 [PATCH] RTC subsy... |
172 173 |
return 0; } |
1da177e4c Linux-2.6.12-rc2 |
174 |
|
8417eb7a1 [PATCH] RTC subsy... |
175 176 177 178 |
static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) { unsigned long alarm_sec; struct rtc_time *time = &wkalrm->time; |
1da177e4c Linux-2.6.12-rc2 |
179 |
|
8417eb7a1 [PATCH] RTC subsy... |
180 181 |
alarm_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec); |
1da177e4c Linux-2.6.12-rc2 |
182 |
|
8417eb7a1 [PATCH] RTC subsy... |
183 |
spin_lock_irq(&rtc_lock); |
1da177e4c Linux-2.6.12-rc2 |
184 |
|
9b5ef64a3 rtc: update vr41x... |
185 |
if (alarm_enabled) |
bd0765098 [MIPS] separate p... |
186 |
disable_irq(aie_irq); |
9b5ef64a3 rtc: update vr41x... |
187 |
|
8417eb7a1 [PATCH] RTC subsy... |
188 189 190 |
rtc1_write(ECMPLREG, (uint16_t)(alarm_sec << 15)); rtc1_write(ECMPMREG, (uint16_t)(alarm_sec >> 1)); rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17)); |
1da177e4c Linux-2.6.12-rc2 |
191 |
|
9b5ef64a3 rtc: update vr41x... |
192 |
if (wkalrm->enabled) |
bd0765098 [MIPS] separate p... |
193 |
enable_irq(aie_irq); |
9b5ef64a3 rtc: update vr41x... |
194 195 |
alarm_enabled = wkalrm->enabled; |
8417eb7a1 [PATCH] RTC subsy... |
196 |
spin_unlock_irq(&rtc_lock); |
1da177e4c Linux-2.6.12-rc2 |
197 198 199 |
return 0; } |
4cad4431f rtc-vr41xx: add i... |
200 201 |
static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { |
1da177e4c Linux-2.6.12-rc2 |
202 |
switch (cmd) { |
1da177e4c Linux-2.6.12-rc2 |
203 204 205 206 207 208 |
case RTC_EPOCH_READ: return put_user(epoch, (unsigned long __user *)arg); case RTC_EPOCH_SET: /* Doesn't support before 1900 */ if (arg < 1900) return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
209 210 211 |
epoch = arg; break; default: |
b3969e583 [PATCH] rtc subsy... |
212 |
return -ENOIOCTLCMD; |
1da177e4c Linux-2.6.12-rc2 |
213 214 215 216 |
} return 0; } |
16380c153 RTC: Convert rtc ... |
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
static int vr41xx_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { spin_lock_irq(&rtc_lock); if (enabled) { if (!alarm_enabled) { enable_irq(aie_irq); alarm_enabled = 1; } } else { if (alarm_enabled) { disable_irq(aie_irq); alarm_enabled = 0; } } spin_unlock_irq(&rtc_lock); return 0; } |
7d12e780e IRQ: Maintain reg... |
234 |
static irqreturn_t elapsedtime_interrupt(int irq, void *dev_id) |
1da177e4c Linux-2.6.12-rc2 |
235 |
{ |
8417eb7a1 [PATCH] RTC subsy... |
236 237 |
struct platform_device *pdev = (struct platform_device *)dev_id; struct rtc_device *rtc = platform_get_drvdata(pdev); |
1da177e4c Linux-2.6.12-rc2 |
238 |
|
8417eb7a1 [PATCH] RTC subsy... |
239 |
rtc2_write(RTCINTREG, ELAPSEDTIME_INT); |
1da177e4c Linux-2.6.12-rc2 |
240 |
|
ab6a2d70d rtc: rtc interfac... |
241 |
rtc_update_irq(rtc, 1, RTC_AF); |
1da177e4c Linux-2.6.12-rc2 |
242 243 244 |
return IRQ_HANDLED; } |
7d12e780e IRQ: Maintain reg... |
245 |
static irqreturn_t rtclong1_interrupt(int irq, void *dev_id) |
1da177e4c Linux-2.6.12-rc2 |
246 |
{ |
8417eb7a1 [PATCH] RTC subsy... |
247 248 |
struct platform_device *pdev = (struct platform_device *)dev_id; struct rtc_device *rtc = platform_get_drvdata(pdev); |
1da177e4c Linux-2.6.12-rc2 |
249 |
unsigned long count = periodic_count; |
1da177e4c Linux-2.6.12-rc2 |
250 251 252 253 |
rtc2_write(RTCINTREG, RTCLONG1_INT); rtc1_write(RTCL1LREG, count); rtc1_write(RTCL1HREG, count >> 16); |
ab6a2d70d rtc: rtc interfac... |
254 |
rtc_update_irq(rtc, 1, RTC_PF); |
1da177e4c Linux-2.6.12-rc2 |
255 256 257 |
return IRQ_HANDLED; } |
ff8371ac9 [PATCH] constify ... |
258 |
static const struct rtc_class_ops vr41xx_rtc_ops = { |
8417eb7a1 [PATCH] RTC subsy... |
259 260 261 262 263 264 |
.release = vr41xx_rtc_release, .ioctl = vr41xx_rtc_ioctl, .read_time = vr41xx_rtc_read_time, .set_time = vr41xx_rtc_set_time, .read_alarm = vr41xx_rtc_read_alarm, .set_alarm = vr41xx_rtc_set_alarm, |
1da177e4c Linux-2.6.12-rc2 |
265 |
}; |
d39b6cfe6 [PATCH] vr41xx: c... |
266 |
static int __devinit rtc_probe(struct platform_device *pdev) |
1da177e4c Linux-2.6.12-rc2 |
267 |
{ |
bd0765098 [MIPS] separate p... |
268 |
struct resource *res; |
8417eb7a1 [PATCH] RTC subsy... |
269 |
struct rtc_device *rtc; |
1da177e4c Linux-2.6.12-rc2 |
270 |
int retval; |
bd0765098 [MIPS] separate p... |
271 |
if (pdev->num_resources != 4) |
1da177e4c Linux-2.6.12-rc2 |
272 |
return -EBUSY; |
bd0765098 [MIPS] separate p... |
273 274 |
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) |
1da177e4c Linux-2.6.12-rc2 |
275 |
return -EBUSY; |
a91912f8e rtc-vr41xx: use r... |
276 |
rtc1_base = ioremap(res->start, resource_size(res)); |
bd0765098 [MIPS] separate p... |
277 |
if (!rtc1_base) |
1da177e4c Linux-2.6.12-rc2 |
278 |
return -EBUSY; |
bd0765098 [MIPS] separate p... |
279 280 281 282 283 284 |
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { retval = -EBUSY; goto err_rtc1_iounmap; } |
a91912f8e rtc-vr41xx: use r... |
285 |
rtc2_base = ioremap(res->start, resource_size(res)); |
bd0765098 [MIPS] separate p... |
286 287 288 |
if (!rtc2_base) { retval = -EBUSY; goto err_rtc1_iounmap; |
1da177e4c Linux-2.6.12-rc2 |
289 |
} |
8417eb7a1 [PATCH] RTC subsy... |
290 291 |
rtc = rtc_device_register(rtc_name, &pdev->dev, &vr41xx_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) { |
bd0765098 [MIPS] separate p... |
292 293 |
retval = PTR_ERR(rtc); goto err_iounmap_all; |
1da177e4c Linux-2.6.12-rc2 |
294 |
} |
4cad4431f rtc-vr41xx: add i... |
295 |
rtc->max_user_freq = MAX_PERIODIC_RATE; |
1da177e4c Linux-2.6.12-rc2 |
296 297 298 299 300 301 302 |
spin_lock_irq(&rtc_lock); rtc1_write(ECMPLREG, 0); rtc1_write(ECMPMREG, 0); rtc1_write(ECMPHREG, 0); rtc1_write(RTCL1LREG, 0); rtc1_write(RTCL1HREG, 0); |
1da177e4c Linux-2.6.12-rc2 |
303 |
spin_unlock_irq(&rtc_lock); |
bd0765098 [MIPS] separate p... |
304 |
aie_irq = platform_get_irq(pdev, 0); |
2fac6674d rtc: bunch of dri... |
305 |
if (aie_irq <= 0) { |
bd0765098 [MIPS] separate p... |
306 307 |
retval = -EBUSY; goto err_device_unregister; |
1da177e4c Linux-2.6.12-rc2 |
308 |
} |
bd0765098 [MIPS] separate p... |
309 310 311 312 313 314 |
retval = request_irq(aie_irq, elapsedtime_interrupt, IRQF_DISABLED, "elapsed_time", pdev); if (retval < 0) goto err_device_unregister; pie_irq = platform_get_irq(pdev, 1); |
2fac6674d rtc: bunch of dri... |
315 |
if (pie_irq <= 0) |
bd0765098 [MIPS] separate p... |
316 317 318 319 320 321 |
goto err_free_irq; retval = request_irq(pie_irq, rtclong1_interrupt, IRQF_DISABLED, "rtclong1", pdev); if (retval < 0) goto err_free_irq; |
1da177e4c Linux-2.6.12-rc2 |
322 |
|
8417eb7a1 [PATCH] RTC subsy... |
323 |
platform_set_drvdata(pdev, rtc); |
bd0765098 [MIPS] separate p... |
324 325 |
disable_irq(aie_irq); disable_irq(pie_irq); |
1da177e4c Linux-2.6.12-rc2 |
326 |
|
1da177e4c Linux-2.6.12-rc2 |
327 328 329 330 |
printk(KERN_INFO "rtc: Real Time Clock of NEC VR4100 series "); return 0; |
bd0765098 [MIPS] separate p... |
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
err_free_irq: free_irq(aie_irq, pdev); err_device_unregister: rtc_device_unregister(rtc); err_iounmap_all: iounmap(rtc2_base); rtc2_base = NULL; err_rtc1_iounmap: iounmap(rtc1_base); rtc1_base = NULL; return retval; |
1da177e4c Linux-2.6.12-rc2 |
347 |
} |
8417eb7a1 [PATCH] RTC subsy... |
348 |
static int __devexit rtc_remove(struct platform_device *pdev) |
1da177e4c Linux-2.6.12-rc2 |
349 |
{ |
8417eb7a1 [PATCH] RTC subsy... |
350 |
struct rtc_device *rtc; |
1da177e4c Linux-2.6.12-rc2 |
351 |
|
8417eb7a1 [PATCH] RTC subsy... |
352 |
rtc = platform_get_drvdata(pdev); |
bd0765098 [MIPS] separate p... |
353 |
if (rtc) |
8417eb7a1 [PATCH] RTC subsy... |
354 355 356 |
rtc_device_unregister(rtc); platform_set_drvdata(pdev, NULL); |
1da177e4c Linux-2.6.12-rc2 |
357 |
|
bd0765098 [MIPS] separate p... |
358 359 360 |
free_irq(aie_irq, pdev); free_irq(pie_irq, pdev); if (rtc1_base) |
1da177e4c Linux-2.6.12-rc2 |
361 |
iounmap(rtc1_base); |
bd0765098 [MIPS] separate p... |
362 |
if (rtc2_base) |
1da177e4c Linux-2.6.12-rc2 |
363 364 365 366 |
iounmap(rtc2_base); return 0; } |
ad28a07bc rtc: fix platform... |
367 368 |
/* work with hotplug and coldplug */ MODULE_ALIAS("platform:RTC"); |
8417eb7a1 [PATCH] RTC subsy... |
369 |
static struct platform_driver rtc_platform_driver = { |
1da177e4c Linux-2.6.12-rc2 |
370 |
.probe = rtc_probe, |
d39b6cfe6 [PATCH] vr41xx: c... |
371 |
.remove = __devexit_p(rtc_remove), |
3ae5eaec1 [DRIVER MODEL] Co... |
372 373 |
.driver = { .name = rtc_name, |
d39b6cfe6 [PATCH] vr41xx: c... |
374 |
.owner = THIS_MODULE, |
3ae5eaec1 [DRIVER MODEL] Co... |
375 |
}, |
1da177e4c Linux-2.6.12-rc2 |
376 |
}; |
0c4eae665 rtc: convert driv... |
377 |
module_platform_driver(rtc_platform_driver); |