/*
* Blackfin On-Chip Real Time Clock Driver
- * Supports BF52[257]/BF53[123]/BF53[467]/BF54[24789]
+ * Supports BF51x/BF52x/BF53[123]/BF53[467]/BF54x
*
- * Copyright 2004-2008 Analog Devices Inc.
+ * Copyright 2004-2009 Analog Devices Inc.
*
* Enter bugs at http://blackfin.uclinux.org/
*
* writes to clear status registers complete immediately.
*/
+/* It may seem odd that there is no SWCNT code in here (which would be exposed
+ * via the periodic interrupt event, or PIE). Since the Blackfin RTC peripheral
+ * runs in units of seconds (N/HZ) but the Linux framework runs in units of HZ
+ * (2^N HZ), there is no point in keeping code that only provides 1 HZ PIEs.
+ * The same exact behavior can be accomplished by using the update interrupt
+ * event (UIE). Maybe down the line the RTC peripheral will suck less in which
+ * case we can re-introduce PIE support.
+ */
+
#include <linux/bcd.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/seq_file.h>
+#include <linux/slab.h>
#include <asm/blackfin.h>
* Initialize the RTC. Enable pre-scaler to scale RTC clock
* to 1Hz and clear interrupt/status registers.
*/
-static void bfin_rtc_reset(struct device *dev)
+static void bfin_rtc_reset(struct device *dev, u16 rtc_ictl)
{
struct bfin_rtc *rtc = dev_get_drvdata(dev);
dev_dbg_stamp(dev);
bfin_rtc_sync_pending(dev);
bfin_write_RTC_PREN(0x1);
- bfin_write_RTC_ICTL(RTC_ISTAT_WRITE_COMPLETE);
- bfin_write_RTC_SWCNT(0);
+ bfin_write_RTC_ICTL(rtc_ictl);
bfin_write_RTC_ALARM(0);
bfin_write_RTC_ISTAT(0xFFFF);
rtc->rtc_wrote_regs = 0;
}
}
- if (rtc_ictl & RTC_ISTAT_STOPWATCH) {
- if (rtc_istat & RTC_ISTAT_STOPWATCH) {
- bfin_write_RTC_ISTAT(RTC_ISTAT_STOPWATCH);
- events |= RTC_PF | RTC_IRQF;
- bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq);
- }
- }
-
if (rtc_ictl & RTC_ISTAT_SEC) {
if (rtc_istat & RTC_ISTAT_SEC) {
bfin_write_RTC_ISTAT(RTC_ISTAT_SEC);
return IRQ_NONE;
}
-static int bfin_rtc_open(struct device *dev)
-{
- int ret;
-
- dev_dbg_stamp(dev);
-
- ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_SHARED, to_platform_device(dev)->name, dev);
- if (!ret)
- bfin_rtc_reset(dev);
-
- return ret;
-}
-
-static void bfin_rtc_release(struct device *dev)
-{
- dev_dbg_stamp(dev);
- bfin_rtc_reset(dev);
- free_irq(IRQ_RTC, dev);
-}
-
static void bfin_rtc_int_set(u16 rtc_int)
{
bfin_write_RTC_ISTAT(rtc_int);
bfin_rtc_sync_pending(dev);
switch (cmd) {
- case RTC_PIE_ON:
- dev_dbg_stamp(dev);
- bfin_rtc_int_set(RTC_ISTAT_STOPWATCH);
- bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq);
- break;
- case RTC_PIE_OFF:
- dev_dbg_stamp(dev);
- bfin_rtc_int_clear(~RTC_ISTAT_STOPWATCH);
- break;
-
case RTC_UIE_ON:
dev_dbg_stamp(dev);
bfin_rtc_int_set(RTC_ISTAT_SEC);
seq_printf(seq,
"alarm_IRQ\t: %s\n"
"wkalarm_IRQ\t: %s\n"
- "seconds_IRQ\t: %s\n"
- "periodic_IRQ\t: %s\n",
+ "seconds_IRQ\t: %s\n",
yesno(ictl & RTC_ISTAT_ALARM),
yesno(ictl & RTC_ISTAT_ALARM_DAY),
- yesno(ictl & RTC_ISTAT_SEC),
- yesno(ictl & RTC_ISTAT_STOPWATCH));
+ yesno(ictl & RTC_ISTAT_SEC));
return 0;
#undef yesno
}
static struct rtc_class_ops bfin_rtc_ops = {
- .open = bfin_rtc_open,
- .release = bfin_rtc_release,
.ioctl = bfin_rtc_ioctl,
.read_time = bfin_rtc_read_time,
.set_time = bfin_rtc_set_time,
static int __devinit bfin_rtc_probe(struct platform_device *pdev)
{
struct bfin_rtc *rtc;
+ struct device *dev = &pdev->dev;
int ret = 0;
+ unsigned long timeout = jiffies + HZ;
- dev_dbg_stamp(&pdev->dev);
+ dev_dbg_stamp(dev);
+ /* Allocate memory for our RTC struct */
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
if (unlikely(!rtc))
return -ENOMEM;
+ platform_set_drvdata(pdev, rtc);
+ device_init_wakeup(dev, 1);
- rtc->rtc_dev = rtc_device_register(pdev->name, &pdev->dev, &bfin_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
+ /* Register our RTC with the RTC framework */
+ rtc->rtc_dev = rtc_device_register(pdev->name, dev, &bfin_rtc_ops,
+ THIS_MODULE);
+ if (unlikely(IS_ERR(rtc->rtc_dev))) {
ret = PTR_ERR(rtc->rtc_dev);
goto err;
}
- rtc->rtc_dev->irq_freq = 1;
-
- platform_set_drvdata(pdev, rtc);
- device_init_wakeup(&pdev->dev, 1);
+ /* Grab the IRQ and init the hardware */
+ ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, 0, pdev->name, dev);
+ if (unlikely(ret))
+ goto err_reg;
+ /* sometimes the bootloader touched things, but the write complete was not
+ * enabled, so let's just do a quick timeout here since the IRQ will not fire ...
+ */
+ while (bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING)
+ if (time_after(jiffies, timeout))
+ break;
+ bfin_rtc_reset(dev, RTC_ISTAT_WRITE_COMPLETE);
+ bfin_write_RTC_SWCNT(0);
return 0;
- err:
+err_reg:
+ rtc_device_unregister(rtc->rtc_dev);
+err:
kfree(rtc);
return ret;
}
static int __devexit bfin_rtc_remove(struct platform_device *pdev)
{
struct bfin_rtc *rtc = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ bfin_rtc_reset(dev, 0);
+ free_irq(IRQ_RTC, dev);
rtc_device_unregister(rtc->rtc_dev);
platform_set_drvdata(pdev, NULL);
kfree(rtc);
#ifdef CONFIG_PM
static int bfin_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
- if (device_may_wakeup(&pdev->dev))
+ if (device_may_wakeup(&pdev->dev)) {
enable_irq_wake(IRQ_RTC);
- else
+ bfin_rtc_sync_pending(&pdev->dev);
+ } else
bfin_rtc_int_clear(-1);
return 0;