Blame view
drivers/rtc/rtc-stk17ta8.c
10.5 KB
029641151 rtc: add support ... |
1 2 3 |
/* * A RTC driver for the Simtek STK17TA8 * |
ab4364d31 stk17ta8: renamin... |
4 |
* By Thomas Hommel <thomas.hommel@ge.com> |
029641151 rtc: add support ... |
5 6 7 8 9 10 11 12 13 14 15 16 |
* * Based on the DS1553 driver from * 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: ... |
17 |
#include <linux/gfp.h> |
029641151 rtc: add support ... |
18 19 20 21 22 23 |
#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... |
24 |
#include <linux/module.h> |
029641151 rtc: add support ... |
25 |
|
029641151 rtc: add support ... |
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 |
#define RTC_REG_SIZE 0x20000 #define RTC_OFFSET 0x1fff0 #define RTC_FLAGS (RTC_OFFSET + 0) #define RTC_CENTURY (RTC_OFFSET + 1) #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_CALIBRATION (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_SECONDS_MASK 0x7f #define RTC_DAY_MASK 0x07 #define RTC_CAL_MASK 0x3f /* Bits in the Calibration register */ #define RTC_STOP 0x80 /* Bits in the Flags register */ #define RTC_FLAGS_AF 0x40 #define RTC_FLAGS_PF 0x20 #define RTC_WRITE 0x02 #define RTC_READ 0x01 /* Bits in the Interrupts register */ #define RTC_INTS_AIE 0x40 struct rtc_plat_data { struct rtc_device *rtc; void __iomem *ioaddr; |
029641151 rtc: add support ... |
65 66 67 68 69 70 71 |
unsigned long last_jiffies; int irq; unsigned int irqen; int alrm_sec; int alrm_min; int alrm_hour; int alrm_mday; |
3151520d8 rtc-stk17ta8: fix... |
72 |
spinlock_t lock; |
029641151 rtc: add support ... |
73 74 75 76 77 78 79 80 81 82 83 |
}; static int stk17ta8_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 flags; flags = readb(pdata->ioaddr + RTC_FLAGS); writeb(flags | RTC_WRITE, pdata->ioaddr + RTC_FLAGS); |
fe20ba70a drivers/rtc/: use... |
84 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); writeb(bin2bcd((tm->tm_year + 1900) / 100), ioaddr + RTC_CENTURY); |
029641151 rtc: add support ... |
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 121 |
writeb(flags & ~RTC_WRITE, pdata->ioaddr + RTC_FLAGS); return 0; } static int stk17ta8_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; u8 flags; /* give enough time to update RTC in case of continuous read */ if (pdata->last_jiffies == jiffies) msleep(1); pdata->last_jiffies = jiffies; flags = readb(pdata->ioaddr + RTC_FLAGS); writeb(flags | RTC_READ, ioaddr + RTC_FLAGS); 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); writeb(flags & ~RTC_READ, ioaddr + RTC_FLAGS); |
fe20ba70a drivers/rtc/: use... |
122 123 124 125 126 127 |
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; |
029641151 rtc: add support ... |
128 |
/* year is 1900 + tm->tm_year */ |
fe20ba70a drivers/rtc/: use... |
129 |
tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900; |
029641151 rtc: add support ... |
130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
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 stk17ta8_rtc_update_alarm(struct rtc_plat_data *pdata) { void __iomem *ioaddr = pdata->ioaddr; unsigned long irqflags; u8 flags; |
3151520d8 rtc-stk17ta8: fix... |
144 |
spin_lock_irqsave(&pdata->lock, irqflags); |
029641151 rtc: add support ... |
145 146 147 148 149 |
flags = readb(ioaddr + RTC_FLAGS); writeb(flags | RTC_WRITE, ioaddr + RTC_FLAGS); writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ? |
fe20ba70a drivers/rtc/: use... |
150 |
0x80 : bin2bcd(pdata->alrm_mday), |
029641151 rtc: add support ... |
151 152 |
ioaddr + RTC_DATE_ALARM); writeb(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ? |
fe20ba70a drivers/rtc/: use... |
153 |
0x80 : bin2bcd(pdata->alrm_hour), |
029641151 rtc: add support ... |
154 155 |
ioaddr + RTC_HOURS_ALARM); writeb(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ? |
fe20ba70a drivers/rtc/: use... |
156 |
0x80 : bin2bcd(pdata->alrm_min), |
029641151 rtc: add support ... |
157 158 |
ioaddr + RTC_MINUTES_ALARM); writeb(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ? |
fe20ba70a drivers/rtc/: use... |
159 |
0x80 : bin2bcd(pdata->alrm_sec), |
029641151 rtc: add support ... |
160 161 162 163 |
ioaddr + RTC_SECONDS_ALARM); writeb(pdata->irqen ? RTC_INTS_AIE : 0, ioaddr + RTC_INTERRUPTS); readb(ioaddr + RTC_FLAGS); /* clear interrupts */ writeb(flags & ~RTC_WRITE, ioaddr + RTC_FLAGS); |
3151520d8 rtc-stk17ta8: fix... |
164 |
spin_unlock_irqrestore(&pdata->lock, irqflags); |
029641151 rtc: add support ... |
165 166 167 168 169 170 |
} static int stk17ta8_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... |
171 |
if (pdata->irq <= 0) |
029641151 rtc: add support ... |
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
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; stk17ta8_rtc_update_alarm(pdata); return 0; } static int stk17ta8_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... |
187 |
if (pdata->irq <= 0) |
029641151 rtc: add support ... |
188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
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; } static irqreturn_t stk17ta8_rtc_interrupt(int irq, void *dev_id) { struct platform_device *pdev = dev_id; struct rtc_plat_data *pdata = platform_get_drvdata(pdev); void __iomem *ioaddr = pdata->ioaddr; |
3151520d8 rtc-stk17ta8: fix... |
202 |
unsigned long events = 0; |
029641151 rtc: add support ... |
203 |
|
3151520d8 rtc-stk17ta8: fix... |
204 |
spin_lock(&pdata->lock); |
029641151 rtc: add support ... |
205 |
/* read and clear interrupt */ |
3151520d8 rtc-stk17ta8: fix... |
206 207 208 209 210 211 |
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; |
0d71915d2 rtc: treewide: re... |
212 |
rtc_update_irq(pdata->rtc, 1, events); |
3151520d8 rtc-stk17ta8: fix... |
213 214 215 |
} spin_unlock(&pdata->lock); return events ? IRQ_HANDLED : IRQ_NONE; |
029641151 rtc: add support ... |
216 |
} |
3151520d8 rtc-stk17ta8: fix... |
217 218 |
static int stk17ta8_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) |
029641151 rtc: add support ... |
219 220 221 |
{ struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); |
2fac6674d rtc: bunch of dri... |
222 |
if (pdata->irq <= 0) |
3151520d8 rtc-stk17ta8: fix... |
223 224 |
return -EINVAL; if (enabled) |
029641151 rtc: add support ... |
225 |
pdata->irqen |= RTC_AF; |
3151520d8 rtc-stk17ta8: fix... |
226 227 228 |
else pdata->irqen &= ~RTC_AF; stk17ta8_rtc_update_alarm(pdata); |
029641151 rtc: add support ... |
229 230 231 232 |
return 0; } static const struct rtc_class_ops stk17ta8_rtc_ops = { |
3151520d8 rtc-stk17ta8: fix... |
233 234 235 236 237 |
.read_time = stk17ta8_rtc_read_time, .set_time = stk17ta8_rtc_set_time, .read_alarm = stk17ta8_rtc_read_alarm, .set_alarm = stk17ta8_rtc_set_alarm, .alarm_irq_enable = stk17ta8_rtc_alarm_irq_enable, |
029641151 rtc: add support ... |
238 |
}; |
2c3c8bea6 sysfs: add struct... |
239 |
static ssize_t stk17ta8_nvram_read(struct file *filp, struct kobject *kobj, |
c98dbe59a fix missing argum... |
240 |
struct bin_attribute *attr, char *buf, |
029641151 rtc: add support ... |
241 242 |
loff_t pos, size_t size) { |
50e49bee3 rtc: do not use c... |
243 244 |
struct device *dev = container_of(kobj, struct device, kobj); struct platform_device *pdev = to_platform_device(dev); |
029641151 rtc: add support ... |
245 246 247 |
struct rtc_plat_data *pdata = platform_get_drvdata(pdev); void __iomem *ioaddr = pdata->ioaddr; ssize_t count; |
ecc663c3d rtc: stk17ta8: cl... |
248 |
for (count = 0; count < size; count++) |
029641151 rtc: add support ... |
249 250 251 |
*buf++ = readb(ioaddr + pos++); return count; } |
2c3c8bea6 sysfs: add struct... |
252 |
static ssize_t stk17ta8_nvram_write(struct file *filp, struct kobject *kobj, |
c98dbe59a fix missing argum... |
253 |
struct bin_attribute *attr, char *buf, |
029641151 rtc: add support ... |
254 255 |
loff_t pos, size_t size) { |
50e49bee3 rtc: do not use c... |
256 257 |
struct device *dev = container_of(kobj, struct device, kobj); struct platform_device *pdev = to_platform_device(dev); |
029641151 rtc: add support ... |
258 259 260 |
struct rtc_plat_data *pdata = platform_get_drvdata(pdev); void __iomem *ioaddr = pdata->ioaddr; ssize_t count; |
ecc663c3d rtc: stk17ta8: cl... |
261 |
for (count = 0; count < size; count++) |
029641151 rtc: add support ... |
262 263 264 265 266 267 268 |
writeb(*buf++, ioaddr + pos++); return count; } static struct bin_attribute stk17ta8_nvram_attr = { .attr = { .name = "nvram", |
a4b1d50e6 RTCs: handle NVRA... |
269 |
.mode = S_IRUGO | S_IWUSR, |
029641151 rtc: add support ... |
270 271 272 273 274 |
}, .size = RTC_OFFSET, .read = stk17ta8_nvram_read, .write = stk17ta8_nvram_write, }; |
5a167f454 Drivers: rtc: rem... |
275 |
static int stk17ta8_rtc_probe(struct platform_device *pdev) |
029641151 rtc: add support ... |
276 |
{ |
029641151 rtc: add support ... |
277 278 279 280 |
struct resource *res; unsigned int cal; unsigned int flags; struct rtc_plat_data *pdata; |
3151520d8 rtc-stk17ta8: fix... |
281 |
void __iomem *ioaddr; |
029641151 rtc: add support ... |
282 |
int ret = 0; |
3151520d8 rtc-stk17ta8: fix... |
283 |
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); |
029641151 rtc: add support ... |
284 285 |
if (!pdata) return -ENOMEM; |
7c1d69ee1 rtc: simplify dev... |
286 287 288 289 290 |
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ioaddr = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(ioaddr)) return PTR_ERR(ioaddr); |
029641151 rtc: add support ... |
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
pdata->ioaddr = ioaddr; pdata->irq = platform_get_irq(pdev, 0); /* turn RTC on if it was not on */ cal = readb(ioaddr + RTC_CALIBRATION); if (cal & RTC_STOP) { cal &= RTC_CAL_MASK; flags = readb(ioaddr + RTC_FLAGS); writeb(flags | RTC_WRITE, ioaddr + RTC_FLAGS); writeb(cal, ioaddr + RTC_CALIBRATION); writeb(flags & ~RTC_WRITE, ioaddr + RTC_FLAGS); } if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_PF) dev_warn(&pdev->dev, "voltage-low detected. "); |
3151520d8 rtc-stk17ta8: fix... |
306 307 308 |
spin_lock_init(&pdata->lock); pdata->last_jiffies = jiffies; platform_set_drvdata(pdev, pdata); |
2fac6674d rtc: bunch of dri... |
309 |
if (pdata->irq > 0) { |
029641151 rtc: add support ... |
310 |
writeb(0, ioaddr + RTC_INTERRUPTS); |
3151520d8 rtc-stk17ta8: fix... |
311 312 |
if (devm_request_irq(&pdev->dev, pdata->irq, stk17ta8_rtc_interrupt, |
2f6e5f945 drivers/rtc: remo... |
313 |
IRQF_SHARED, |
029641151 rtc: add support ... |
314 315 316 |
pdev->name, pdev) < 0) { dev_warn(&pdev->dev, "interrupt not available. "); |
2fac6674d rtc: bunch of dri... |
317 |
pdata->irq = 0; |
029641151 rtc: add support ... |
318 319 |
} } |
9c6813d7e rtc: rtc-stk17ta8... |
320 |
pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, |
029641151 rtc: add support ... |
321 |
&stk17ta8_rtc_ops, THIS_MODULE); |
3151520d8 rtc-stk17ta8: fix... |
322 323 |
if (IS_ERR(pdata->rtc)) return PTR_ERR(pdata->rtc); |
b74d2caa6 rtc: fix driver d... |
324 |
|
029641151 rtc: add support ... |
325 |
ret = sysfs_create_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr); |
9c6813d7e rtc: rtc-stk17ta8... |
326 |
|
029641151 rtc: add support ... |
327 328 |
return ret; } |
5a167f454 Drivers: rtc: rem... |
329 |
static int stk17ta8_rtc_remove(struct platform_device *pdev) |
029641151 rtc: add support ... |
330 331 332 333 |
{ struct rtc_plat_data *pdata = platform_get_drvdata(pdev); sysfs_remove_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr); |
3151520d8 rtc-stk17ta8: fix... |
334 |
if (pdata->irq > 0) |
029641151 rtc: add support ... |
335 |
writeb(0, pdata->ioaddr + RTC_INTERRUPTS); |
029641151 rtc: add support ... |
336 337 |
return 0; } |
ad28a07bc rtc: fix platform... |
338 339 |
/* work with hotplug and coldplug */ MODULE_ALIAS("platform:stk17ta8"); |
029641151 rtc: add support ... |
340 341 |
static struct platform_driver stk17ta8_rtc_driver = { .probe = stk17ta8_rtc_probe, |
5a167f454 Drivers: rtc: rem... |
342 |
.remove = stk17ta8_rtc_remove, |
029641151 rtc: add support ... |
343 344 |
.driver = { .name = "stk17ta8", |
029641151 rtc: add support ... |
345 346 |
}, }; |
0c4eae665 rtc: convert driv... |
347 |
module_platform_driver(stk17ta8_rtc_driver); |
029641151 rtc: add support ... |
348 |
|
ab4364d31 stk17ta8: renamin... |
349 |
MODULE_AUTHOR("Thomas Hommel <thomas.hommel@ge.com>"); |
029641151 rtc: add support ... |
350 351 |
MODULE_DESCRIPTION("Simtek STK17TA8 RTC driver"); MODULE_LICENSE("GPL"); |