diff options
author | Yauhen Kharuzhy <jekhor@gmail.com> | 2012-01-10 18:10:34 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-10 19:30:52 -0500 |
commit | c92182ee0b5a33c74e4b6c0ded36166e4ef3bc3e (patch) | |
tree | 4583c06816318f4e4b4216239f83d91aa11b5fbe /drivers | |
parent | 7287be1d0ac8c82999b67c2a33517c6ec9cfdbe7 (diff) |
drivers/rtc/rtc-mxc.c: make alarm work
Fix alarm IRQ handling, make the alarm one-shot. Cleanup black magick
with a validation of already validated time data.
Add ability to wake the system with alarm.
[akpm@linux-foundation.org: fix CONFIG_PM=n build]
Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
Cc: Daniel Mack <daniel@caiaq.de>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/rtc/rtc-mxc.c | 112 |
1 files changed, 59 insertions, 53 deletions
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index 11b7b614fc8d..5e1d64ee5228 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c | |||
@@ -155,7 +155,6 @@ static int rtc_update_alarm(struct device *dev, struct rtc_time *alrm) | |||
155 | { | 155 | { |
156 | struct rtc_time alarm_tm, now_tm; | 156 | struct rtc_time alarm_tm, now_tm; |
157 | unsigned long now, time; | 157 | unsigned long now, time; |
158 | int ret; | ||
159 | struct platform_device *pdev = to_platform_device(dev); | 158 | struct platform_device *pdev = to_platform_device(dev); |
160 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | 159 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); |
161 | void __iomem *ioaddr = pdata->ioaddr; | 160 | void __iomem *ioaddr = pdata->ioaddr; |
@@ -168,21 +167,33 @@ static int rtc_update_alarm(struct device *dev, struct rtc_time *alrm) | |||
168 | alarm_tm.tm_hour = alrm->tm_hour; | 167 | alarm_tm.tm_hour = alrm->tm_hour; |
169 | alarm_tm.tm_min = alrm->tm_min; | 168 | alarm_tm.tm_min = alrm->tm_min; |
170 | alarm_tm.tm_sec = alrm->tm_sec; | 169 | alarm_tm.tm_sec = alrm->tm_sec; |
171 | rtc_tm_to_time(&now_tm, &now); | ||
172 | rtc_tm_to_time(&alarm_tm, &time); | 170 | rtc_tm_to_time(&alarm_tm, &time); |
173 | 171 | ||
174 | if (time < now) { | ||
175 | time += 60 * 60 * 24; | ||
176 | rtc_time_to_tm(time, &alarm_tm); | ||
177 | } | ||
178 | |||
179 | ret = rtc_tm_to_time(&alarm_tm, &time); | ||
180 | |||
181 | /* clear all the interrupt status bits */ | 172 | /* clear all the interrupt status bits */ |
182 | writew(readw(ioaddr + RTC_RTCISR), ioaddr + RTC_RTCISR); | 173 | writew(readw(ioaddr + RTC_RTCISR), ioaddr + RTC_RTCISR); |
183 | set_alarm_or_time(dev, MXC_RTC_ALARM, time); | 174 | set_alarm_or_time(dev, MXC_RTC_ALARM, time); |
184 | 175 | ||
185 | return ret; | 176 | return 0; |
177 | } | ||
178 | |||
179 | static void mxc_rtc_irq_enable(struct device *dev, unsigned int bit, | ||
180 | unsigned int enabled) | ||
181 | { | ||
182 | struct platform_device *pdev = to_platform_device(dev); | ||
183 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | ||
184 | void __iomem *ioaddr = pdata->ioaddr; | ||
185 | u32 reg; | ||
186 | |||
187 | spin_lock_irq(&pdata->rtc->irq_lock); | ||
188 | reg = readw(ioaddr + RTC_RTCIENR); | ||
189 | |||
190 | if (enabled) | ||
191 | reg |= bit; | ||
192 | else | ||
193 | reg &= ~bit; | ||
194 | |||
195 | writew(reg, ioaddr + RTC_RTCIENR); | ||
196 | spin_unlock_irq(&pdata->rtc->irq_lock); | ||
186 | } | 197 | } |
187 | 198 | ||
188 | /* This function is the RTC interrupt service routine. */ | 199 | /* This function is the RTC interrupt service routine. */ |
@@ -199,13 +210,12 @@ static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id) | |||
199 | /* clear interrupt sources */ | 210 | /* clear interrupt sources */ |
200 | writew(status, ioaddr + RTC_RTCISR); | 211 | writew(status, ioaddr + RTC_RTCISR); |
201 | 212 | ||
202 | /* clear alarm interrupt if it has occurred */ | ||
203 | if (status & RTC_ALM_BIT) | ||
204 | status &= ~RTC_ALM_BIT; | ||
205 | |||
206 | /* update irq data & counter */ | 213 | /* update irq data & counter */ |
207 | if (status & RTC_ALM_BIT) | 214 | if (status & RTC_ALM_BIT) { |
208 | events |= (RTC_AF | RTC_IRQF); | 215 | events |= (RTC_AF | RTC_IRQF); |
216 | /* RTC alarm should be one-shot */ | ||
217 | mxc_rtc_irq_enable(&pdev->dev, RTC_ALM_BIT, 0); | ||
218 | } | ||
209 | 219 | ||
210 | if (status & RTC_1HZ_BIT) | 220 | if (status & RTC_1HZ_BIT) |
211 | events |= (RTC_UF | RTC_IRQF); | 221 | events |= (RTC_UF | RTC_IRQF); |
@@ -213,9 +223,6 @@ static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id) | |||
213 | if (status & PIT_ALL_ON) | 223 | if (status & PIT_ALL_ON) |
214 | events |= (RTC_PF | RTC_IRQF); | 224 | events |= (RTC_PF | RTC_IRQF); |
215 | 225 | ||
216 | if ((status & RTC_ALM_BIT) && rtc_valid_tm(&pdata->g_rtc_alarm)) | ||
217 | rtc_update_alarm(&pdev->dev, &pdata->g_rtc_alarm); | ||
218 | |||
219 | rtc_update_irq(pdata->rtc, 1, events); | 226 | rtc_update_irq(pdata->rtc, 1, events); |
220 | spin_unlock_irq(&pdata->rtc->irq_lock); | 227 | spin_unlock_irq(&pdata->rtc->irq_lock); |
221 | 228 | ||
@@ -242,26 +249,6 @@ static void mxc_rtc_release(struct device *dev) | |||
242 | spin_unlock_irq(&pdata->rtc->irq_lock); | 249 | spin_unlock_irq(&pdata->rtc->irq_lock); |
243 | } | 250 | } |
244 | 251 | ||
245 | static void mxc_rtc_irq_enable(struct device *dev, unsigned int bit, | ||
246 | unsigned int enabled) | ||
247 | { | ||
248 | struct platform_device *pdev = to_platform_device(dev); | ||
249 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | ||
250 | void __iomem *ioaddr = pdata->ioaddr; | ||
251 | u32 reg; | ||
252 | |||
253 | spin_lock_irq(&pdata->rtc->irq_lock); | ||
254 | reg = readw(ioaddr + RTC_RTCIENR); | ||
255 | |||
256 | if (enabled) | ||
257 | reg |= bit; | ||
258 | else | ||
259 | reg &= ~bit; | ||
260 | |||
261 | writew(reg, ioaddr + RTC_RTCIENR); | ||
262 | spin_unlock_irq(&pdata->rtc->irq_lock); | ||
263 | } | ||
264 | |||
265 | static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) | 252 | static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) |
266 | { | 253 | { |
267 | mxc_rtc_irq_enable(dev, RTC_ALM_BIT, enabled); | 254 | mxc_rtc_irq_enable(dev, RTC_ALM_BIT, enabled); |
@@ -335,21 +322,7 @@ static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
335 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | 322 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); |
336 | int ret; | 323 | int ret; |
337 | 324 | ||
338 | if (rtc_valid_tm(&alrm->time)) { | 325 | ret = rtc_update_alarm(dev, &alrm->time); |
339 | if (alrm->time.tm_sec > 59 || | ||
340 | alrm->time.tm_hour > 23 || | ||
341 | alrm->time.tm_min > 59) | ||
342 | return -EINVAL; | ||
343 | |||
344 | ret = rtc_update_alarm(dev, &alrm->time); | ||
345 | } else { | ||
346 | ret = rtc_valid_tm(&alrm->time); | ||
347 | if (ret) | ||
348 | return ret; | ||
349 | |||
350 | ret = rtc_update_alarm(dev, &alrm->time); | ||
351 | } | ||
352 | |||
353 | if (ret) | 326 | if (ret) |
354 | return ret; | 327 | return ret; |
355 | 328 | ||
@@ -435,6 +408,9 @@ static int __init mxc_rtc_probe(struct platform_device *pdev) | |||
435 | pdata->irq = -1; | 408 | pdata->irq = -1; |
436 | } | 409 | } |
437 | 410 | ||
411 | if (pdata->irq >=0) | ||
412 | device_init_wakeup(&pdev->dev, 1); | ||
413 | |||
438 | rtc = rtc_device_register(pdev->name, &pdev->dev, &mxc_rtc_ops, | 414 | rtc = rtc_device_register(pdev->name, &pdev->dev, &mxc_rtc_ops, |
439 | THIS_MODULE); | 415 | THIS_MODULE); |
440 | if (IS_ERR(rtc)) { | 416 | if (IS_ERR(rtc)) { |
@@ -470,9 +446,39 @@ static int __exit mxc_rtc_remove(struct platform_device *pdev) | |||
470 | return 0; | 446 | return 0; |
471 | } | 447 | } |
472 | 448 | ||
449 | #ifdef CONFIG_PM | ||
450 | static int mxc_rtc_suspend(struct device *dev) | ||
451 | { | ||
452 | struct rtc_plat_data *pdata = dev_get_drvdata(dev); | ||
453 | |||
454 | if (device_may_wakeup(dev)) | ||
455 | enable_irq_wake(pdata->irq); | ||
456 | |||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | static int mxc_rtc_resume(struct device *dev) | ||
461 | { | ||
462 | struct rtc_plat_data *pdata = dev_get_drvdata(dev); | ||
463 | |||
464 | if (device_may_wakeup(dev)) | ||
465 | disable_irq_wake(pdata->irq); | ||
466 | |||
467 | return 0; | ||
468 | } | ||
469 | |||
470 | static struct dev_pm_ops mxc_rtc_pm_ops = { | ||
471 | .suspend = mxc_rtc_suspend, | ||
472 | .resume = mxc_rtc_resume, | ||
473 | }; | ||
474 | #endif | ||
475 | |||
473 | static struct platform_driver mxc_rtc_driver = { | 476 | static struct platform_driver mxc_rtc_driver = { |
474 | .driver = { | 477 | .driver = { |
475 | .name = "mxc_rtc", | 478 | .name = "mxc_rtc", |
479 | #ifdef CONFIG_PM | ||
480 | .pm = &mxc_rtc_pm_ops, | ||
481 | #endif | ||
476 | .owner = THIS_MODULE, | 482 | .owner = THIS_MODULE, |
477 | }, | 483 | }, |
478 | .remove = __exit_p(mxc_rtc_remove), | 484 | .remove = __exit_p(mxc_rtc_remove), |