Blame view
drivers/rtc/rtc-ds1553.c
10.7 KB
9bf5b4f5f [PATCH] RTC: add ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* * An rtc driver for the Dallas DS1553 * * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/bcd.h> #include <linux/init.h> #include <linux/kernel.h> |
5a0e3ad6a include cleanup: ... |
14 |
#include <linux/gfp.h> |
9bf5b4f5f [PATCH] RTC: add ... |
15 16 17 18 19 20 |
#include <linux/delay.h> #include <linux/jiffies.h> #include <linux/interrupt.h> #include <linux/rtc.h> #include <linux/platform_device.h> #include <linux/io.h> |
2113852b2 rtc: Add module.h... |
21 |
#include <linux/module.h> |
9bf5b4f5f [PATCH] RTC: add ... |
22 |
|
618161f71 rtc-ds1553: fix r... |
23 |
#define DRV_VERSION "0.3" |
9bf5b4f5f [PATCH] RTC: add ... |
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 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 |
#define RTC_REG_SIZE 0x2000 #define RTC_OFFSET 0x1ff0 #define RTC_FLAGS (RTC_OFFSET + 0) #define RTC_SECONDS_ALARM (RTC_OFFSET + 2) #define RTC_MINUTES_ALARM (RTC_OFFSET + 3) #define RTC_HOURS_ALARM (RTC_OFFSET + 4) #define RTC_DATE_ALARM (RTC_OFFSET + 5) #define RTC_INTERRUPTS (RTC_OFFSET + 6) #define RTC_WATCHDOG (RTC_OFFSET + 7) #define RTC_CONTROL (RTC_OFFSET + 8) #define RTC_CENTURY (RTC_OFFSET + 8) #define RTC_SECONDS (RTC_OFFSET + 9) #define RTC_MINUTES (RTC_OFFSET + 10) #define RTC_HOURS (RTC_OFFSET + 11) #define RTC_DAY (RTC_OFFSET + 12) #define RTC_DATE (RTC_OFFSET + 13) #define RTC_MONTH (RTC_OFFSET + 14) #define RTC_YEAR (RTC_OFFSET + 15) #define RTC_CENTURY_MASK 0x3f #define RTC_SECONDS_MASK 0x7f #define RTC_DAY_MASK 0x07 /* Bits in the Control/Century register */ #define RTC_WRITE 0x80 #define RTC_READ 0x40 /* Bits in the Seconds register */ #define RTC_STOP 0x80 /* Bits in the Flags register */ #define RTC_FLAGS_AF 0x40 #define RTC_FLAGS_BLF 0x10 /* Bits in the Interrupts register */ #define RTC_INTS_AE 0x80 struct rtc_plat_data { struct rtc_device *rtc; void __iomem *ioaddr; |
9bf5b4f5f [PATCH] RTC: add ... |
66 67 68 69 70 71 72 |
unsigned long last_jiffies; int irq; unsigned int irqen; int alrm_sec; int alrm_min; int alrm_hour; int alrm_mday; |
618161f71 rtc-ds1553: fix r... |
73 |
spinlock_t lock; |
9bf5b4f5f [PATCH] RTC: add ... |
74 75 76 77 78 79 80 81 |
}; static int ds1553_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); void __iomem *ioaddr = pdata->ioaddr; u8 century; |
fe20ba70a drivers/rtc/: use... |
82 |
century = bin2bcd((tm->tm_year + 1900) / 100); |
9bf5b4f5f [PATCH] RTC: add ... |
83 84 |
writeb(RTC_WRITE, pdata->ioaddr + RTC_CONTROL); |
fe20ba70a drivers/rtc/: use... |
85 86 87 88 89 90 91 |
writeb(bin2bcd(tm->tm_year % 100), ioaddr + RTC_YEAR); writeb(bin2bcd(tm->tm_mon + 1), ioaddr + RTC_MONTH); writeb(bin2bcd(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY); writeb(bin2bcd(tm->tm_mday), ioaddr + RTC_DATE); writeb(bin2bcd(tm->tm_hour), ioaddr + RTC_HOURS); writeb(bin2bcd(tm->tm_min), ioaddr + RTC_MINUTES); writeb(bin2bcd(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS); |
9bf5b4f5f [PATCH] RTC: add ... |
92 93 94 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 120 |
/* RTC_CENTURY and RTC_CONTROL share same register */ writeb(RTC_WRITE | (century & RTC_CENTURY_MASK), ioaddr + RTC_CENTURY); writeb(century & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL); return 0; } static int ds1553_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); void __iomem *ioaddr = pdata->ioaddr; unsigned int year, month, day, hour, minute, second, week; unsigned int century; /* give enough time to update RTC in case of continuous read */ if (pdata->last_jiffies == jiffies) msleep(1); pdata->last_jiffies = jiffies; writeb(RTC_READ, ioaddr + RTC_CONTROL); second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK; minute = readb(ioaddr + RTC_MINUTES); hour = readb(ioaddr + RTC_HOURS); day = readb(ioaddr + RTC_DATE); week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK; month = readb(ioaddr + RTC_MONTH); year = readb(ioaddr + RTC_YEAR); century = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK; writeb(0, ioaddr + RTC_CONTROL); |
fe20ba70a drivers/rtc/: use... |
121 122 123 124 125 126 |
tm->tm_sec = bcd2bin(second); tm->tm_min = bcd2bin(minute); tm->tm_hour = bcd2bin(hour); tm->tm_mday = bcd2bin(day); tm->tm_wday = bcd2bin(week); tm->tm_mon = bcd2bin(month) - 1; |
9bf5b4f5f [PATCH] RTC: add ... |
127 |
/* year is 1900 + tm->tm_year */ |
fe20ba70a drivers/rtc/: use... |
128 |
tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900; |
9bf5b4f5f [PATCH] RTC: add ... |
129 130 131 132 133 134 135 136 137 138 139 140 141 |
if (rtc_valid_tm(tm) < 0) { dev_err(dev, "retrieved date/time is not valid. "); rtc_time_to_tm(0, tm); } return 0; } static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata) { void __iomem *ioaddr = pdata->ioaddr; unsigned long flags; |
618161f71 rtc-ds1553: fix r... |
142 |
spin_lock_irqsave(&pdata->lock, flags); |
9bf5b4f5f [PATCH] RTC: add ... |
143 |
writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ? |
fe20ba70a drivers/rtc/: use... |
144 |
0x80 : bin2bcd(pdata->alrm_mday), |
9bf5b4f5f [PATCH] RTC: add ... |
145 146 |
ioaddr + RTC_DATE_ALARM); writeb(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ? |
fe20ba70a drivers/rtc/: use... |
147 |
0x80 : bin2bcd(pdata->alrm_hour), |
9bf5b4f5f [PATCH] RTC: add ... |
148 149 |
ioaddr + RTC_HOURS_ALARM); writeb(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ? |
fe20ba70a drivers/rtc/: use... |
150 |
0x80 : bin2bcd(pdata->alrm_min), |
9bf5b4f5f [PATCH] RTC: add ... |
151 152 |
ioaddr + RTC_MINUTES_ALARM); writeb(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ? |
fe20ba70a drivers/rtc/: use... |
153 |
0x80 : bin2bcd(pdata->alrm_sec), |
9bf5b4f5f [PATCH] RTC: add ... |
154 155 156 |
ioaddr + RTC_SECONDS_ALARM); writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS); readb(ioaddr + RTC_FLAGS); /* clear interrupts */ |
618161f71 rtc-ds1553: fix r... |
157 |
spin_unlock_irqrestore(&pdata->lock, flags); |
9bf5b4f5f [PATCH] RTC: add ... |
158 159 160 161 162 163 |
} static int ds1553_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); |
2fac6674d rtc: bunch of dri... |
164 |
if (pdata->irq <= 0) |
9bf5b4f5f [PATCH] RTC: add ... |
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
return -EINVAL; pdata->alrm_mday = alrm->time.tm_mday; pdata->alrm_hour = alrm->time.tm_hour; pdata->alrm_min = alrm->time.tm_min; pdata->alrm_sec = alrm->time.tm_sec; if (alrm->enabled) pdata->irqen |= RTC_AF; ds1553_rtc_update_alarm(pdata); return 0; } static int ds1553_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); |
2fac6674d rtc: bunch of dri... |
180 |
if (pdata->irq <= 0) |
9bf5b4f5f [PATCH] RTC: add ... |
181 182 183 184 185 186 187 188 |
return -EINVAL; alrm->time.tm_mday = pdata->alrm_mday < 0 ? 0 : pdata->alrm_mday; alrm->time.tm_hour = pdata->alrm_hour < 0 ? 0 : pdata->alrm_hour; alrm->time.tm_min = pdata->alrm_min < 0 ? 0 : pdata->alrm_min; alrm->time.tm_sec = pdata->alrm_sec < 0 ? 0 : pdata->alrm_sec; alrm->enabled = (pdata->irqen & RTC_AF) ? 1 : 0; return 0; } |
7d12e780e IRQ: Maintain reg... |
189 |
static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id) |
9bf5b4f5f [PATCH] RTC: add ... |
190 191 192 193 |
{ struct platform_device *pdev = dev_id; struct rtc_plat_data *pdata = platform_get_drvdata(pdev); void __iomem *ioaddr = pdata->ioaddr; |
618161f71 rtc-ds1553: fix r... |
194 |
unsigned long events = 0; |
9bf5b4f5f [PATCH] RTC: add ... |
195 |
|
618161f71 rtc-ds1553: fix r... |
196 |
spin_lock(&pdata->lock); |
9bf5b4f5f [PATCH] RTC: add ... |
197 |
/* read and clear interrupt */ |
618161f71 rtc-ds1553: fix r... |
198 199 200 201 202 203 204 205 206 207 208 |
if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) { events = RTC_IRQF; if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80) events |= RTC_UF; else events |= RTC_AF; if (likely(pdata->rtc)) rtc_update_irq(pdata->rtc, 1, events); } spin_unlock(&pdata->lock); return events ? IRQ_HANDLED : IRQ_NONE; |
9bf5b4f5f [PATCH] RTC: add ... |
209 |
} |
618161f71 rtc-ds1553: fix r... |
210 |
static int ds1553_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) |
9bf5b4f5f [PATCH] RTC: add ... |
211 212 213 |
{ struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); |
2fac6674d rtc: bunch of dri... |
214 |
if (pdata->irq <= 0) |
618161f71 rtc-ds1553: fix r... |
215 216 |
return -EINVAL; if (enabled) |
9bf5b4f5f [PATCH] RTC: add ... |
217 |
pdata->irqen |= RTC_AF; |
618161f71 rtc-ds1553: fix r... |
218 219 220 221 222 |
else pdata->irqen &= ~RTC_AF; ds1553_rtc_update_alarm(pdata); return 0; } |
ff8371ac9 [PATCH] constify ... |
223 |
static const struct rtc_class_ops ds1553_rtc_ops = { |
618161f71 rtc-ds1553: fix r... |
224 225 226 227 228 |
.read_time = ds1553_rtc_read_time, .set_time = ds1553_rtc_set_time, .read_alarm = ds1553_rtc_read_alarm, .set_alarm = ds1553_rtc_set_alarm, .alarm_irq_enable = ds1553_rtc_alarm_irq_enable, |
9bf5b4f5f [PATCH] RTC: add ... |
229 |
}; |
2c3c8bea6 sysfs: add struct... |
230 |
static ssize_t ds1553_nvram_read(struct file *filp, struct kobject *kobj, |
91a690295 sysfs: add parame... |
231 232 |
struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) |
9bf5b4f5f [PATCH] RTC: add ... |
233 |
{ |
50e49bee3 rtc: do not use c... |
234 235 |
struct device *dev = container_of(kobj, struct device, kobj); struct platform_device *pdev = to_platform_device(dev); |
9bf5b4f5f [PATCH] RTC: add ... |
236 237 238 239 240 241 242 243 |
struct rtc_plat_data *pdata = platform_get_drvdata(pdev); void __iomem *ioaddr = pdata->ioaddr; ssize_t count; for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--) *buf++ = readb(ioaddr + pos++); return count; } |
2c3c8bea6 sysfs: add struct... |
244 |
static ssize_t ds1553_nvram_write(struct file *filp, struct kobject *kobj, |
91a690295 sysfs: add parame... |
245 246 |
struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) |
9bf5b4f5f [PATCH] RTC: add ... |
247 |
{ |
50e49bee3 rtc: do not use c... |
248 249 |
struct device *dev = container_of(kobj, struct device, kobj); struct platform_device *pdev = to_platform_device(dev); |
9bf5b4f5f [PATCH] RTC: add ... |
250 251 252 253 254 255 256 257 258 259 260 261 |
struct rtc_plat_data *pdata = platform_get_drvdata(pdev); void __iomem *ioaddr = pdata->ioaddr; ssize_t count; for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--) writeb(*buf++, ioaddr + pos++); return count; } static struct bin_attribute ds1553_nvram_attr = { .attr = { .name = "nvram", |
a4b1d50e6 RTCs: handle NVRA... |
262 |
.mode = S_IRUGO | S_IWUSR, |
9bf5b4f5f [PATCH] RTC: add ... |
263 264 265 266 267 |
}, .size = RTC_OFFSET, .read = ds1553_nvram_read, .write = ds1553_nvram_write, }; |
c239122de [PATCH] change __... |
268 |
static int __devinit ds1553_rtc_probe(struct platform_device *pdev) |
9bf5b4f5f [PATCH] RTC: add ... |
269 270 271 272 |
{ struct rtc_device *rtc; struct resource *res; unsigned int cen, sec; |
618161f71 rtc-ds1553: fix r... |
273 274 |
struct rtc_plat_data *pdata; void __iomem *ioaddr; |
9bf5b4f5f [PATCH] RTC: add ... |
275 276 277 278 279 |
int ret = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; |
618161f71 rtc-ds1553: fix r... |
280 |
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); |
9bf5b4f5f [PATCH] RTC: add ... |
281 282 |
if (!pdata) return -ENOMEM; |
618161f71 rtc-ds1553: fix r... |
283 284 285 286 287 288 289 |
if (!devm_request_mem_region(&pdev->dev, res->start, RTC_REG_SIZE, pdev->name)) return -EBUSY; ioaddr = devm_ioremap(&pdev->dev, res->start, RTC_REG_SIZE); if (!ioaddr) return -ENOMEM; |
9bf5b4f5f [PATCH] RTC: add ... |
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
pdata->ioaddr = ioaddr; pdata->irq = platform_get_irq(pdev, 0); /* turn RTC on if it was not on */ sec = readb(ioaddr + RTC_SECONDS); if (sec & RTC_STOP) { sec &= RTC_SECONDS_MASK; cen = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK; writeb(RTC_WRITE, ioaddr + RTC_CONTROL); writeb(sec, ioaddr + RTC_SECONDS); writeb(cen & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL); } if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_BLF) dev_warn(&pdev->dev, "voltage-low detected. "); |
618161f71 rtc-ds1553: fix r... |
305 306 307 |
spin_lock_init(&pdata->lock); pdata->last_jiffies = jiffies; platform_set_drvdata(pdev, pdata); |
2fac6674d rtc: bunch of dri... |
308 |
if (pdata->irq > 0) { |
9bf5b4f5f [PATCH] RTC: add ... |
309 |
writeb(0, ioaddr + RTC_INTERRUPTS); |
618161f71 rtc-ds1553: fix r... |
310 311 |
if (devm_request_irq(&pdev->dev, pdata->irq, ds1553_rtc_interrupt, |
014b6e90a rtc-ds1553: drop ... |
312 |
IRQF_DISABLED, pdev->name, pdev) < 0) { |
9bf5b4f5f [PATCH] RTC: add ... |
313 314 |
dev_warn(&pdev->dev, "interrupt not available. "); |
2fac6674d rtc: bunch of dri... |
315 |
pdata->irq = 0; |
9bf5b4f5f [PATCH] RTC: add ... |
316 317 318 319 320 |
} } rtc = rtc_device_register(pdev->name, &pdev->dev, &ds1553_rtc_ops, THIS_MODULE); |
618161f71 rtc-ds1553: fix r... |
321 322 |
if (IS_ERR(rtc)) return PTR_ERR(rtc); |
9bf5b4f5f [PATCH] RTC: add ... |
323 |
pdata->rtc = rtc; |
618161f71 rtc-ds1553: fix r... |
324 |
|
391b1fe67 [PATCH] RTC: rtc-... |
325 326 |
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr); if (ret) |
618161f71 rtc-ds1553: fix r... |
327 |
rtc_device_unregister(rtc); |
9bf5b4f5f [PATCH] RTC: add ... |
328 329 330 331 332 333 334 335 336 |
return ret; } static int __devexit ds1553_rtc_remove(struct platform_device *pdev) { struct rtc_plat_data *pdata = platform_get_drvdata(pdev); sysfs_remove_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr); rtc_device_unregister(pdata->rtc); |
618161f71 rtc-ds1553: fix r... |
337 |
if (pdata->irq > 0) |
9bf5b4f5f [PATCH] RTC: add ... |
338 |
writeb(0, pdata->ioaddr + RTC_INTERRUPTS); |
9bf5b4f5f [PATCH] RTC: add ... |
339 340 |
return 0; } |
ad28a07bc rtc: fix platform... |
341 342 |
/* work with hotplug and coldplug */ MODULE_ALIAS("platform:rtc-ds1553"); |
9bf5b4f5f [PATCH] RTC: add ... |
343 344 345 346 |
static struct platform_driver ds1553_rtc_driver = { .probe = ds1553_rtc_probe, .remove = __devexit_p(ds1553_rtc_remove), .driver = { |
e7634c271 rtc: make rtc-ds1... |
347 |
.name = "rtc-ds1553", |
9bf5b4f5f [PATCH] RTC: add ... |
348 349 350 |
.owner = THIS_MODULE, }, }; |
0c4eae665 rtc: convert driv... |
351 |
module_platform_driver(ds1553_rtc_driver); |
9bf5b4f5f [PATCH] RTC: add ... |
352 353 354 355 356 |
MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>"); MODULE_DESCRIPTION("Dallas DS1553 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); |