Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.30-rc4 323 lines 7.5 kB view raw
1/* 2 * An RTC driver for the AVR32 AT32AP700x processor series. 3 * 4 * Copyright (C) 2007 Atmel Corporation 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 as published 8 * by the Free Software Foundation. 9 */ 10 11#include <linux/module.h> 12#include <linux/kernel.h> 13#include <linux/platform_device.h> 14#include <linux/rtc.h> 15#include <linux/io.h> 16 17/* 18 * This is a bare-bones RTC. It runs during most system sleep states, but has 19 * no battery backup and gets reset during system restart. It must be 20 * initialized from an external clock (network, I2C, etc) before it can be of 21 * much use. 22 * 23 * The alarm functionality is limited by the hardware, not supporting 24 * periodic interrupts. 25 */ 26 27#define RTC_CTRL 0x00 28#define RTC_CTRL_EN 0 29#define RTC_CTRL_PCLR 1 30#define RTC_CTRL_TOPEN 2 31#define RTC_CTRL_PSEL 8 32 33#define RTC_VAL 0x04 34 35#define RTC_TOP 0x08 36 37#define RTC_IER 0x10 38#define RTC_IER_TOPI 0 39 40#define RTC_IDR 0x14 41#define RTC_IDR_TOPI 0 42 43#define RTC_IMR 0x18 44#define RTC_IMR_TOPI 0 45 46#define RTC_ISR 0x1c 47#define RTC_ISR_TOPI 0 48 49#define RTC_ICR 0x20 50#define RTC_ICR_TOPI 0 51 52#define RTC_BIT(name) (1 << RTC_##name) 53#define RTC_BF(name, value) ((value) << RTC_##name) 54 55#define rtc_readl(dev, reg) \ 56 __raw_readl((dev)->regs + RTC_##reg) 57#define rtc_writel(dev, reg, value) \ 58 __raw_writel((value), (dev)->regs + RTC_##reg) 59 60struct rtc_at32ap700x { 61 struct rtc_device *rtc; 62 void __iomem *regs; 63 unsigned long alarm_time; 64 unsigned long irq; 65 /* Protect against concurrent register access. */ 66 spinlock_t lock; 67}; 68 69static int at32_rtc_readtime(struct device *dev, struct rtc_time *tm) 70{ 71 struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); 72 unsigned long now; 73 74 now = rtc_readl(rtc, VAL); 75 rtc_time_to_tm(now, tm); 76 77 return 0; 78} 79 80static int at32_rtc_settime(struct device *dev, struct rtc_time *tm) 81{ 82 struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); 83 unsigned long now; 84 int ret; 85 86 ret = rtc_tm_to_time(tm, &now); 87 if (ret == 0) 88 rtc_writel(rtc, VAL, now); 89 90 return ret; 91} 92 93static int at32_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) 94{ 95 struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); 96 97 spin_lock_irq(&rtc->lock); 98 rtc_time_to_tm(rtc->alarm_time, &alrm->time); 99 alrm->enabled = rtc_readl(rtc, IMR) & RTC_BIT(IMR_TOPI) ? 1 : 0; 100 alrm->pending = rtc_readl(rtc, ISR) & RTC_BIT(ISR_TOPI) ? 1 : 0; 101 spin_unlock_irq(&rtc->lock); 102 103 return 0; 104} 105 106static int at32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) 107{ 108 struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); 109 unsigned long rtc_unix_time; 110 unsigned long alarm_unix_time; 111 int ret; 112 113 rtc_unix_time = rtc_readl(rtc, VAL); 114 115 ret = rtc_tm_to_time(&alrm->time, &alarm_unix_time); 116 if (ret) 117 return ret; 118 119 if (alarm_unix_time < rtc_unix_time) 120 return -EINVAL; 121 122 spin_lock_irq(&rtc->lock); 123 rtc->alarm_time = alarm_unix_time; 124 rtc_writel(rtc, TOP, rtc->alarm_time); 125 if (alrm->enabled) 126 rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) 127 | RTC_BIT(CTRL_TOPEN)); 128 else 129 rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) 130 & ~RTC_BIT(CTRL_TOPEN)); 131 spin_unlock_irq(&rtc->lock); 132 133 return ret; 134} 135 136static int at32_rtc_ioctl(struct device *dev, unsigned int cmd, 137 unsigned long arg) 138{ 139 struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); 140 int ret = 0; 141 142 spin_lock_irq(&rtc->lock); 143 144 switch (cmd) { 145 case RTC_AIE_ON: 146 if (rtc_readl(rtc, VAL) > rtc->alarm_time) { 147 ret = -EINVAL; 148 break; 149 } 150 rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) 151 | RTC_BIT(CTRL_TOPEN)); 152 rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI)); 153 rtc_writel(rtc, IER, RTC_BIT(IER_TOPI)); 154 break; 155 case RTC_AIE_OFF: 156 rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) 157 & ~RTC_BIT(CTRL_TOPEN)); 158 rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI)); 159 rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI)); 160 break; 161 default: 162 ret = -ENOIOCTLCMD; 163 break; 164 } 165 166 spin_unlock_irq(&rtc->lock); 167 168 return ret; 169} 170 171static irqreturn_t at32_rtc_interrupt(int irq, void *dev_id) 172{ 173 struct rtc_at32ap700x *rtc = (struct rtc_at32ap700x *)dev_id; 174 unsigned long isr = rtc_readl(rtc, ISR); 175 unsigned long events = 0; 176 int ret = IRQ_NONE; 177 178 spin_lock(&rtc->lock); 179 180 if (isr & RTC_BIT(ISR_TOPI)) { 181 rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI)); 182 rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI)); 183 rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) 184 & ~RTC_BIT(CTRL_TOPEN)); 185 rtc_writel(rtc, VAL, rtc->alarm_time); 186 events = RTC_AF | RTC_IRQF; 187 rtc_update_irq(rtc->rtc, 1, events); 188 ret = IRQ_HANDLED; 189 } 190 191 spin_unlock(&rtc->lock); 192 193 return ret; 194} 195 196static struct rtc_class_ops at32_rtc_ops = { 197 .ioctl = at32_rtc_ioctl, 198 .read_time = at32_rtc_readtime, 199 .set_time = at32_rtc_settime, 200 .read_alarm = at32_rtc_readalarm, 201 .set_alarm = at32_rtc_setalarm, 202}; 203 204static int __init at32_rtc_probe(struct platform_device *pdev) 205{ 206 struct resource *regs; 207 struct rtc_at32ap700x *rtc; 208 int irq; 209 int ret; 210 211 rtc = kzalloc(sizeof(struct rtc_at32ap700x), GFP_KERNEL); 212 if (!rtc) { 213 dev_dbg(&pdev->dev, "out of memory\n"); 214 return -ENOMEM; 215 } 216 217 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 218 if (!regs) { 219 dev_dbg(&pdev->dev, "no mmio resource defined\n"); 220 ret = -ENXIO; 221 goto out; 222 } 223 224 irq = platform_get_irq(pdev, 0); 225 if (irq <= 0) { 226 dev_dbg(&pdev->dev, "could not get irq\n"); 227 ret = -ENXIO; 228 goto out; 229 } 230 231 rtc->irq = irq; 232 rtc->regs = ioremap(regs->start, regs->end - regs->start + 1); 233 if (!rtc->regs) { 234 ret = -ENOMEM; 235 dev_dbg(&pdev->dev, "could not map I/O memory\n"); 236 goto out; 237 } 238 spin_lock_init(&rtc->lock); 239 240 /* 241 * Maybe init RTC: count from zero at 1 Hz, disable wrap irq. 242 * 243 * Do not reset VAL register, as it can hold an old time 244 * from last JTAG reset. 245 */ 246 if (!(rtc_readl(rtc, CTRL) & RTC_BIT(CTRL_EN))) { 247 rtc_writel(rtc, CTRL, RTC_BIT(CTRL_PCLR)); 248 rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI)); 249 rtc_writel(rtc, CTRL, RTC_BF(CTRL_PSEL, 0xe) 250 | RTC_BIT(CTRL_EN)); 251 } 252 253 ret = request_irq(irq, at32_rtc_interrupt, IRQF_SHARED, "rtc", rtc); 254 if (ret) { 255 dev_dbg(&pdev->dev, "could not request irq %d\n", irq); 256 goto out_iounmap; 257 } 258 259 rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, 260 &at32_rtc_ops, THIS_MODULE); 261 if (IS_ERR(rtc->rtc)) { 262 dev_dbg(&pdev->dev, "could not register rtc device\n"); 263 ret = PTR_ERR(rtc->rtc); 264 goto out_free_irq; 265 } 266 267 platform_set_drvdata(pdev, rtc); 268 device_init_wakeup(&pdev->dev, 1); 269 270 dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n", 271 (unsigned long)rtc->regs, rtc->irq); 272 273 return 0; 274 275out_free_irq: 276 free_irq(irq, rtc); 277out_iounmap: 278 iounmap(rtc->regs); 279out: 280 kfree(rtc); 281 return ret; 282} 283 284static int __exit at32_rtc_remove(struct platform_device *pdev) 285{ 286 struct rtc_at32ap700x *rtc = platform_get_drvdata(pdev); 287 288 device_init_wakeup(&pdev->dev, 0); 289 290 free_irq(rtc->irq, rtc); 291 iounmap(rtc->regs); 292 rtc_device_unregister(rtc->rtc); 293 kfree(rtc); 294 platform_set_drvdata(pdev, NULL); 295 296 return 0; 297} 298 299MODULE_ALIAS("platform:at32ap700x_rtc"); 300 301static struct platform_driver at32_rtc_driver = { 302 .remove = __exit_p(at32_rtc_remove), 303 .driver = { 304 .name = "at32ap700x_rtc", 305 .owner = THIS_MODULE, 306 }, 307}; 308 309static int __init at32_rtc_init(void) 310{ 311 return platform_driver_probe(&at32_rtc_driver, at32_rtc_probe); 312} 313module_init(at32_rtc_init); 314 315static void __exit at32_rtc_exit(void) 316{ 317 platform_driver_unregister(&at32_rtc_driver); 318} 319module_exit(at32_rtc_exit); 320 321MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); 322MODULE_DESCRIPTION("Real time clock for AVR32 AT32AP700x"); 323MODULE_LICENSE("GPL");