diff options
Diffstat (limited to 'drivers/rtc/rtc-bfin.c')
-rw-r--r-- | drivers/rtc/rtc-bfin.c | 448 |
1 files changed, 0 insertions, 448 deletions
diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c deleted file mode 100644 index 15344b7c07c5..000000000000 --- a/drivers/rtc/rtc-bfin.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Blackfin On-Chip Real Time Clock Driver - * Supports BF51x/BF52x/BF53[123]/BF53[467]/BF54x - * - * Copyright 2004-2010 Analog Devices Inc. - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -/* The biggest issue we deal with in this driver is that register writes are - * synced to the RTC frequency of 1Hz. So if you write to a register and - * attempt to write again before the first write has completed, the new write - * is simply discarded. This can easily be troublesome if userspace disables - * one event (say periodic) and then right after enables an event (say alarm). - * Since all events are maintained in the same interrupt mask register, if - * we wrote to it to disable the first event and then wrote to it again to - * enable the second event, that second event would not be enabled as the - * write would be discarded and things quickly fall apart. - * - * To keep this delay from significantly degrading performance (we, in theory, - * would have to sleep for up to 1 second every time we wanted to write a - * register), we only check the write pending status before we start to issue - * a new write. We bank on the idea that it doesn't matter when the sync - * happens so long as we don't attempt another write before it does. The only - * time userspace would take this penalty is when they try and do multiple - * operations right after another ... but in this case, they need to take the - * sync penalty, so we should be OK. - * - * Also note that the RTC_ISTAT register does not suffer this penalty; its - * 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/init.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/rtc.h> -#include <linux/seq_file.h> -#include <linux/slab.h> - -#include <asm/blackfin.h> - -#define dev_dbg_stamp(dev) dev_dbg(dev, "%s:%i: here i am\n", __func__, __LINE__) - -struct bfin_rtc { - struct rtc_device *rtc_dev; - struct rtc_time rtc_alarm; - u16 rtc_wrote_regs; -}; - -/* Bit values for the ISTAT / ICTL registers */ -#define RTC_ISTAT_WRITE_COMPLETE 0x8000 -#define RTC_ISTAT_WRITE_PENDING 0x4000 -#define RTC_ISTAT_ALARM_DAY 0x0040 -#define RTC_ISTAT_24HR 0x0020 -#define RTC_ISTAT_HOUR 0x0010 -#define RTC_ISTAT_MIN 0x0008 -#define RTC_ISTAT_SEC 0x0004 -#define RTC_ISTAT_ALARM 0x0002 -#define RTC_ISTAT_STOPWATCH 0x0001 - -/* Shift values for RTC_STAT register */ -#define DAY_BITS_OFF 17 -#define HOUR_BITS_OFF 12 -#define MIN_BITS_OFF 6 -#define SEC_BITS_OFF 0 - -/* Some helper functions to convert between the common RTC notion of time - * and the internal Blackfin notion that is encoded in 32bits. - */ -static inline u32 rtc_time_to_bfin(unsigned long now) -{ - u32 sec = (now % 60); - u32 min = (now % (60 * 60)) / 60; - u32 hour = (now % (60 * 60 * 24)) / (60 * 60); - u32 days = (now / (60 * 60 * 24)); - return (sec << SEC_BITS_OFF) + - (min << MIN_BITS_OFF) + - (hour << HOUR_BITS_OFF) + - (days << DAY_BITS_OFF); -} -static inline unsigned long rtc_bfin_to_time(u32 rtc_bfin) -{ - return (((rtc_bfin >> SEC_BITS_OFF) & 0x003F)) + - (((rtc_bfin >> MIN_BITS_OFF) & 0x003F) * 60) + - (((rtc_bfin >> HOUR_BITS_OFF) & 0x001F) * 60 * 60) + - (((rtc_bfin >> DAY_BITS_OFF) & 0x7FFF) * 60 * 60 * 24); -} -static inline void rtc_bfin_to_tm(u32 rtc_bfin, struct rtc_time *tm) -{ - rtc_time_to_tm(rtc_bfin_to_time(rtc_bfin), tm); -} - -/** - * bfin_rtc_sync_pending - make sure pending writes have complete - * - * Wait for the previous write to a RTC register to complete. - * Unfortunately, we can't sleep here as that introduces a race condition when - * turning on interrupt events. Consider this: - * - process sets alarm - * - process enables alarm - * - process sleeps while waiting for rtc write to sync - * - interrupt fires while process is sleeping - * - interrupt acks the event by writing to ISTAT - * - interrupt sets the WRITE PENDING bit - * - interrupt handler finishes - * - process wakes up, sees WRITE PENDING bit set, goes to sleep - * - interrupt fires while process is sleeping - * If anyone can point out the obvious solution here, i'm listening :). This - * shouldn't be an issue on an SMP or preempt system as this function should - * only be called with the rtc lock held. - * - * Other options: - * - disable PREN so the sync happens at 32.768kHZ ... but this changes the - * inc rate for all RTC registers from 1HZ to 32.768kHZ ... - * - use the write complete IRQ - */ -/* -static void bfin_rtc_sync_pending_polled(void) -{ - while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_COMPLETE)) - if (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING)) - break; - bfin_write_RTC_ISTAT(RTC_ISTAT_WRITE_COMPLETE); -} -*/ -static DECLARE_COMPLETION(bfin_write_complete); -static void bfin_rtc_sync_pending(struct device *dev) -{ - dev_dbg_stamp(dev); - while (bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING) - wait_for_completion_timeout(&bfin_write_complete, HZ * 5); - dev_dbg_stamp(dev); -} - -/** - * bfin_rtc_reset - set RTC to sane/known state - * - * 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, 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_ictl); - bfin_write_RTC_ALARM(0); - bfin_write_RTC_ISTAT(0xFFFF); - rtc->rtc_wrote_regs = 0; -} - -/** - * bfin_rtc_interrupt - handle interrupt from RTC - * - * Since we handle all RTC events here, we have to make sure the requested - * interrupt is enabled (in RTC_ICTL) as the event status register (RTC_ISTAT) - * always gets updated regardless of the interrupt being enabled. So when one - * even we care about (e.g. stopwatch) goes off, we don't want to turn around - * and say that other events have happened as well (e.g. second). We do not - * have to worry about pending writes to the RTC_ICTL register as interrupts - * only fire if they are enabled in the RTC_ICTL register. - */ -static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id) -{ - struct device *dev = dev_id; - struct bfin_rtc *rtc = dev_get_drvdata(dev); - unsigned long events = 0; - bool write_complete = false; - u16 rtc_istat, rtc_istat_clear, rtc_ictl, bits; - - dev_dbg_stamp(dev); - - rtc_istat = bfin_read_RTC_ISTAT(); - rtc_ictl = bfin_read_RTC_ICTL(); - rtc_istat_clear = 0; - - bits = RTC_ISTAT_WRITE_COMPLETE; - if (rtc_istat & bits) { - rtc_istat_clear |= bits; - write_complete = true; - complete(&bfin_write_complete); - } - - bits = (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY); - if (rtc_ictl & bits) { - if (rtc_istat & bits) { - rtc_istat_clear |= bits; - events |= RTC_AF | RTC_IRQF; - } - } - - bits = RTC_ISTAT_SEC; - if (rtc_ictl & bits) { - if (rtc_istat & bits) { - rtc_istat_clear |= bits; - events |= RTC_UF | RTC_IRQF; - } - } - - if (events) - rtc_update_irq(rtc->rtc_dev, 1, events); - - if (write_complete || events) { - bfin_write_RTC_ISTAT(rtc_istat_clear); - return IRQ_HANDLED; - } else - return IRQ_NONE; -} - -static void bfin_rtc_int_set(u16 rtc_int) -{ - bfin_write_RTC_ISTAT(rtc_int); - bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | rtc_int); -} -static void bfin_rtc_int_clear(u16 rtc_int) -{ - bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & rtc_int); -} -static void bfin_rtc_int_set_alarm(struct bfin_rtc *rtc) -{ - /* Blackfin has different bits for whether the alarm is - * more than 24 hours away. - */ - bfin_rtc_int_set(rtc->rtc_alarm.tm_yday == -1 ? RTC_ISTAT_ALARM : RTC_ISTAT_ALARM_DAY); -} - -static int bfin_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) -{ - struct bfin_rtc *rtc = dev_get_drvdata(dev); - - dev_dbg_stamp(dev); - if (enabled) - bfin_rtc_int_set_alarm(rtc); - else - bfin_rtc_int_clear(~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); - - return 0; -} - -static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm) -{ - struct bfin_rtc *rtc = dev_get_drvdata(dev); - - dev_dbg_stamp(dev); - - if (rtc->rtc_wrote_regs & 0x1) - bfin_rtc_sync_pending(dev); - - rtc_bfin_to_tm(bfin_read_RTC_STAT(), tm); - - return 0; -} - -static int bfin_rtc_set_time(struct device *dev, struct rtc_time *tm) -{ - struct bfin_rtc *rtc = dev_get_drvdata(dev); - int ret; - unsigned long now; - - dev_dbg_stamp(dev); - - ret = rtc_tm_to_time(tm, &now); - if (ret == 0) { - if (rtc->rtc_wrote_regs & 0x1) - bfin_rtc_sync_pending(dev); - bfin_write_RTC_STAT(rtc_time_to_bfin(now)); - rtc->rtc_wrote_regs = 0x1; - } - - return ret; -} - -static int bfin_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) -{ - struct bfin_rtc *rtc = dev_get_drvdata(dev); - dev_dbg_stamp(dev); - alrm->time = rtc->rtc_alarm; - bfin_rtc_sync_pending(dev); - alrm->enabled = !!(bfin_read_RTC_ICTL() & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); - return 0; -} - -static int bfin_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) -{ - struct bfin_rtc *rtc = dev_get_drvdata(dev); - unsigned long rtc_alarm; - - dev_dbg_stamp(dev); - - if (rtc_tm_to_time(&alrm->time, &rtc_alarm)) - return -EINVAL; - - rtc->rtc_alarm = alrm->time; - - bfin_rtc_sync_pending(dev); - bfin_write_RTC_ALARM(rtc_time_to_bfin(rtc_alarm)); - if (alrm->enabled) - bfin_rtc_int_set_alarm(rtc); - - return 0; -} - -static int bfin_rtc_proc(struct device *dev, struct seq_file *seq) -{ -#define yesno(x) ((x) ? "yes" : "no") - u16 ictl = bfin_read_RTC_ICTL(); - dev_dbg_stamp(dev); - seq_printf(seq, - "alarm_IRQ\t: %s\n" - "wkalarm_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)); - return 0; -#undef yesno -} - -static const struct rtc_class_ops bfin_rtc_ops = { - .read_time = bfin_rtc_read_time, - .set_time = bfin_rtc_set_time, - .read_alarm = bfin_rtc_read_alarm, - .set_alarm = bfin_rtc_set_alarm, - .proc = bfin_rtc_proc, - .alarm_irq_enable = bfin_rtc_alarm_irq_enable, -}; - -static int bfin_rtc_probe(struct platform_device *pdev) -{ - struct bfin_rtc *rtc; - struct device *dev = &pdev->dev; - int ret; - unsigned long timeout = jiffies + HZ; - - dev_dbg_stamp(dev); - - /* Allocate memory for our RTC struct */ - rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL); - if (unlikely(!rtc)) - return -ENOMEM; - platform_set_drvdata(pdev, rtc); - device_init_wakeup(dev, 1); - - /* Register our RTC with the RTC framework */ - rtc->rtc_dev = devm_rtc_device_register(dev, pdev->name, &bfin_rtc_ops, - THIS_MODULE); - if (IS_ERR(rtc->rtc_dev)) - return PTR_ERR(rtc->rtc_dev); - - /* Grab the IRQ and init the hardware */ - ret = devm_request_irq(dev, IRQ_RTC, bfin_rtc_interrupt, 0, - pdev->name, dev); - if (unlikely(ret)) - dev_err(&pdev->dev, - "unable to request IRQ; alarm won't work, " - "and writes will be delayed\n"); - - /* 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; -} - -static int bfin_rtc_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - - bfin_rtc_reset(dev, 0); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int bfin_rtc_suspend(struct device *dev) -{ - dev_dbg_stamp(dev); - - if (device_may_wakeup(dev)) { - enable_irq_wake(IRQ_RTC); - bfin_rtc_sync_pending(dev); - } else - bfin_rtc_int_clear(0); - - return 0; -} - -static int bfin_rtc_resume(struct device *dev) -{ - dev_dbg_stamp(dev); - - if (device_may_wakeup(dev)) - disable_irq_wake(IRQ_RTC); - - /* - * Since only some of the RTC bits are maintained externally in the - * Vbat domain, we need to wait for the RTC MMRs to be synced into - * the core after waking up. This happens every RTC 1HZ. Once that - * has happened, we can go ahead and re-enable the important write - * complete interrupt event. - */ - while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_SEC)) - continue; - bfin_rtc_int_set(RTC_ISTAT_WRITE_COMPLETE); - - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(bfin_rtc_pm_ops, bfin_rtc_suspend, bfin_rtc_resume); - -static struct platform_driver bfin_rtc_driver = { - .driver = { - .name = "rtc-bfin", - .pm = &bfin_rtc_pm_ops, - }, - .probe = bfin_rtc_probe, - .remove = bfin_rtc_remove, -}; - -module_platform_driver(bfin_rtc_driver); - -MODULE_DESCRIPTION("Blackfin On-Chip Real Time Clock Driver"); -MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:rtc-bfin"); |