From 28f65c11f2ffb3957259dece647a24f8ad2e241b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 9 Jun 2011 09:13:32 -0700 Subject: treewide: Convert uses of struct resource to resource_size(ptr) Several fixes as well where the +1 was missing. Done via coccinelle scripts like: @@ struct resource *ptr; @@ - ptr->end - ptr->start + 1 + resource_size(ptr) and some grep and typing. Mostly uncompiled, no cross-compilers. Signed-off-by: Joe Perches Signed-off-by: Jiri Kosina --- drivers/rtc/rtc-at32ap700x.c | 2 +- drivers/rtc/rtc-cmos.c | 6 +++--- drivers/rtc/rtc-ds1286.c | 2 +- drivers/rtc/rtc-ds1511.c | 2 +- drivers/rtc/rtc-ds1742.c | 2 +- drivers/rtc/rtc-m48t35.c | 2 +- drivers/rtc/rtc-m48t59.c | 2 +- drivers/rtc/rtc-mrst.c | 5 ++--- drivers/rtc/rtc-puv3.c | 5 ++--- drivers/rtc/rtc-s3c.c | 5 ++--- 10 files changed, 15 insertions(+), 18 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c index e725d51e773d..8dd08305aae1 100644 --- a/drivers/rtc/rtc-at32ap700x.c +++ b/drivers/rtc/rtc-at32ap700x.c @@ -223,7 +223,7 @@ static int __init at32_rtc_probe(struct platform_device *pdev) } rtc->irq = irq; - rtc->regs = ioremap(regs->start, regs->end - regs->start + 1); + rtc->regs = ioremap(regs->start, resource_size(regs)); if (!rtc->regs) { ret = -ENOMEM; dev_dbg(&pdev->dev, "could not map I/O memory\n"); diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 911e75cdc125..05beb6c1ca79 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -606,7 +606,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) * (needing ioremap etc), not i/o space resources like this ... */ ports = request_region(ports->start, - ports->end + 1 - ports->start, + resource_size(ports), driver_name); if (!ports) { dev_dbg(dev, "i/o registers already in use\n"); @@ -750,7 +750,7 @@ cleanup1: cmos_rtc.dev = NULL; rtc_device_unregister(cmos_rtc.rtc); cleanup0: - release_region(ports->start, ports->end + 1 - ports->start); + release_region(ports->start, resource_size(ports)); return retval; } @@ -779,7 +779,7 @@ static void __exit cmos_do_remove(struct device *dev) cmos->rtc = NULL; ports = cmos->iomem; - release_region(ports->start, ports->end + 1 - ports->start); + release_region(ports->start, resource_size(ports)); cmos->iomem = NULL; cmos->dev = NULL; diff --git a/drivers/rtc/rtc-ds1286.c b/drivers/rtc/rtc-ds1286.c index 47e681df31e2..68e6caf25496 100644 --- a/drivers/rtc/rtc-ds1286.c +++ b/drivers/rtc/rtc-ds1286.c @@ -343,7 +343,7 @@ static int __devinit ds1286_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->size = res->end - res->start + 1; + priv->size = resource_size(res); if (!request_mem_region(res->start, priv->size, pdev->name)) { ret = -EBUSY; goto out; diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index fbabc773dded..568ad30617e7 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -490,7 +490,7 @@ ds1511_rtc_probe(struct platform_device *pdev) pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; - pdata->size = res->end - res->start + 1; + pdata->size = resource_size(res); if (!devm_request_mem_region(&pdev->dev, res->start, pdata->size, pdev->name)) return -EBUSY; diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c index 042630c90dd3..d84a448dd754 100644 --- a/drivers/rtc/rtc-ds1742.c +++ b/drivers/rtc/rtc-ds1742.c @@ -173,7 +173,7 @@ static int __devinit ds1742_rtc_probe(struct platform_device *pdev) pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; - pdata->size = res->end - res->start + 1; + pdata->size = resource_size(res); if (!devm_request_mem_region(&pdev->dev, res->start, pdata->size, pdev->name)) return -EBUSY; diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c index 7410875e5838..8e2a24e33ed6 100644 --- a/drivers/rtc/rtc-m48t35.c +++ b/drivers/rtc/rtc-m48t35.c @@ -154,7 +154,7 @@ static int __devinit m48t35_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->size = res->end - res->start + 1; + priv->size = resource_size(res); /* * kludge: remove the #ifndef after ioc3 resource * conflicts are resolved diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index 3978f4caf724..28365388fb6c 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -433,7 +433,7 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) if (!m48t59->ioaddr) { /* ioaddr not mapped externally */ - m48t59->ioaddr = ioremap(res->start, res->end - res->start + 1); + m48t59->ioaddr = ioremap(res->start, resource_size(res)); if (!m48t59->ioaddr) goto out; } diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c index 0cec5650d56a..d33544802a2e 100644 --- a/drivers/rtc/rtc-mrst.c +++ b/drivers/rtc/rtc-mrst.c @@ -332,9 +332,8 @@ vrtc_mrst_do_probe(struct device *dev, struct resource *iomem, int rtc_irq) if (!iomem) return -ENODEV; - iomem = request_mem_region(iomem->start, - iomem->end + 1 - iomem->start, - driver_name); + iomem = request_mem_region(iomem->start, resource_size(iomem), + driver_name); if (!iomem) { dev_dbg(dev, "i/o mem already in use.\n"); return -EBUSY; diff --git a/drivers/rtc/rtc-puv3.c b/drivers/rtc/rtc-puv3.c index 46f14b82f3ab..b3eba3cddd42 100644 --- a/drivers/rtc/rtc-puv3.c +++ b/drivers/rtc/rtc-puv3.c @@ -267,9 +267,8 @@ static int puv3_rtc_probe(struct platform_device *pdev) return -ENOENT; } - puv3_rtc_mem = request_mem_region(res->start, - res->end-res->start+1, - pdev->name); + puv3_rtc_mem = request_mem_region(res->start, resource_size(res), + pdev->name); if (puv3_rtc_mem == NULL) { dev_err(&pdev->dev, "failed to reserve memory region\n"); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 16512ecae31a..2a65e85e0f56 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -455,8 +455,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) return -ENOENT; } - s3c_rtc_mem = request_mem_region(res->start, - res->end-res->start+1, + s3c_rtc_mem = request_mem_region(res->start, resource_size(res), pdev->name); if (s3c_rtc_mem == NULL) { @@ -465,7 +464,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) goto err_nores; } - s3c_rtc_base = ioremap(res->start, res->end - res->start + 1); + s3c_rtc_base = ioremap(res->start, resource_size(res)); if (s3c_rtc_base == NULL) { dev_err(&pdev->dev, "failed ioremap()\n"); ret = -EINVAL; -- cgit v1.2.3 From 3dcad5ff08f65ae30832220a0e0ee2eac3502a1a Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 27 May 2011 11:33:18 -0700 Subject: rtc: Avoid accumulating time drift in suspend/resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the RTC interface is only a second granular interface, each time we read from the RTC for suspend/resume, we introduce a half second (on average) of error. In order to avoid this error accumulating as the system is suspended over and over, this patch measures the time delta between the RTC and the system CLOCK_REALTIME. If the delta is less then 2 seconds from the last suspend, we compensate by using the previous time delta (keeping it close). If it is larger then 2 seconds, we assume the clock was set or has been changed, so we do no correction and update the delta. Note: If NTP is running, ths could seem to "fight" with the NTP corrected time, where as if the system time was off by 1 second, and NTP slewed the value in, a suspend/resume cycle could undo this correction, by trying to restore the previous offset from the RTC. However, without this patch, since each read could cause almost a full second worth of error, its possible to get almost 2 seconds of error just from the suspend/resume cycle alone, so this about equal to any offset added by the compensation. Further on systems that suspend/resume frequently, this should keep time closer then NTP could compensate for if the errors were allowed to accumulate. Credits to Arve Hjønnevåg for suggesting this solution. This patch also improves some of the variable names and adds more clear comments. CC: Arve Hjønnevåg CC: Thomas Gleixner Signed-off-by: John Stultz --- drivers/rtc/class.c | 65 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 17 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 4194e59e14cd..01a7df5317c1 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -41,20 +41,41 @@ static void rtc_device_release(struct device *dev) * system's wall clock; restore it on resume(). */ -static time_t oldtime; -static struct timespec oldts; +static struct timespec old_rtc, old_system, old_delta; + static int rtc_suspend(struct device *dev, pm_message_t mesg) { struct rtc_device *rtc = to_rtc_device(dev); struct rtc_time tm; - + struct timespec delta, delta_delta; if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) return 0; + /* snapshot the current RTC and system time at suspend*/ rtc_read_time(rtc, &tm); - ktime_get_ts(&oldts); - rtc_tm_to_time(&tm, &oldtime); + getnstimeofday(&old_system); + rtc_tm_to_time(&tm, &old_rtc.tv_sec); + + + /* + * To avoid drift caused by repeated suspend/resumes, + * which each can add ~1 second drift error, + * try to compensate so the difference in system time + * and rtc time stays close to constant. + */ + delta = timespec_sub(old_system, old_rtc); + delta_delta = timespec_sub(delta, old_delta); + if (abs(delta_delta.tv_sec) >= 2) { + /* + * if delta_delta is too large, assume time correction + * has occured and set old_delta to the current delta. + */ + old_delta = delta; + } else { + /* Otherwise try to adjust old_system to compensate */ + old_system = timespec_sub(old_system, delta_delta); + } return 0; } @@ -63,32 +84,42 @@ static int rtc_resume(struct device *dev) { struct rtc_device *rtc = to_rtc_device(dev); struct rtc_time tm; - time_t newtime; - struct timespec time; - struct timespec newts; + struct timespec new_system, new_rtc; + struct timespec sleep_time; if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) return 0; - ktime_get_ts(&newts); + /* snapshot the current rtc and system time at resume */ + getnstimeofday(&new_system); rtc_read_time(rtc, &tm); if (rtc_valid_tm(&tm) != 0) { pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev)); return 0; } - rtc_tm_to_time(&tm, &newtime); - if (newtime <= oldtime) { - if (newtime < oldtime) + rtc_tm_to_time(&tm, &new_rtc.tv_sec); + new_rtc.tv_nsec = 0; + + if (new_rtc.tv_sec <= old_rtc.tv_sec) { + if (new_rtc.tv_sec < old_rtc.tv_sec) pr_debug("%s: time travel!\n", dev_name(&rtc->dev)); return 0; } - /* calculate the RTC time delta */ - set_normalized_timespec(&time, newtime - oldtime, 0); - /* subtract kernel time between rtc_suspend to rtc_resume */ - time = timespec_sub(time, timespec_sub(newts, oldts)); + /* calculate the RTC time delta (sleep time)*/ + sleep_time = timespec_sub(new_rtc, old_rtc); + + /* + * Since these RTC suspend/resume handlers are not called + * at the very end of suspend or the start of resume, + * some run-time may pass on either sides of the sleep time + * so subtract kernel run-time between rtc_suspend to rtc_resume + * to keep things accurate. + */ + sleep_time = timespec_sub(sleep_time, + timespec_sub(new_system, old_system)); - timekeeping_inject_sleeptime(&time); + timekeeping_inject_sleeptime(&sleep_time); return 0; } -- cgit v1.2.3 From 07187ee13f2b4ba183d4a4832ea153dbb2e030d3 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 22 Jun 2011 23:20:14 +0200 Subject: rtc: vt8500: Use define instead of hardcoded value for status bit Fixes the vt8500 driver to use a define value for the isr is-alarm bit. Cc: Alexey Charkov Signed-off-by: Wolfram Sang Signed-off-by: John Stultz --- drivers/rtc/rtc-vt8500.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c index efd6066b5cd2..f93f412423c6 100644 --- a/drivers/rtc/rtc-vt8500.c +++ b/drivers/rtc/rtc-vt8500.c @@ -74,6 +74,8 @@ #define VT8500_RTC_CR_SM_SEC (1 << 3) /* 0: 1Hz/60, 1: 1Hz */ #define VT8500_RTC_CR_CALIB (1 << 4) /* Enable calibration */ +#define VT8500_RTC_IS_ALARM (1 << 0) /* Alarm interrupt status */ + struct vt8500_rtc { void __iomem *regbase; struct resource *res; @@ -96,7 +98,7 @@ static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id) spin_unlock(&vt8500_rtc->lock); - if (isr & 1) + if (isr & VT8500_RTC_IS_ALARM) events |= RTC_AF | RTC_IRQF; rtc_update_irq(vt8500_rtc->rtc, 1, events); @@ -161,8 +163,8 @@ static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK)); alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0; + alrm->pending = (isr & VT8500_RTC_IS_ALARM) ? 1 : 0; - alrm->pending = (isr & 1) ? 1 : 0; return rtc_valid_tm(&alrm->time); } -- cgit v1.2.3 From 47eac337cd87464b90d0ad2eb2a060036a95b649 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 25 May 2011 12:56:49 +0200 Subject: rtc: stmp3xxx: Restore register definitions The stmp3xxx driver used to include register names from a stmp-specific include. Because of consolidation, plat-stmp has now been removed and merged with the compatible mach-mxs. Restore the register names directly in the driver and rename them to be specific to this driver. Signed-off-by: Wolfram Sang Tested-by: Shawn Guo Signed-off-by: John Stultz --- drivers/rtc/rtc-stmp3xxx.c | 97 ++++++++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 37 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index 572e9534b591..e54bea076067 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -26,7 +26,25 @@ #include #include -#include + +#define STMP3XXX_RTC_CTRL 0x0 +#define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN 0x00000001 +#define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002 +#define STMP3XXX_RTC_CTRL_ALARM_IRQ 0x00000004 +#define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ 0x00000008 + +#define STMP3XXX_RTC_STAT 0x10 +#define STMP3XXX_RTC_STAT_STALE_SHIFT 16 +#define STMP3XXX_RTC_STAT_RTC_PRESENT 0x80000000 + +#define STMP3XXX_RTC_SECONDS 0x30 + +#define STMP3XXX_RTC_ALARM 0x40 + +#define STMP3XXX_RTC_PERSISTENT0 0x60 +#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN 0x00000002 +#define STMP3XXX_RTC_PERSISTENT0_ALARM_EN 0x00000004 +#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE 0x00000080 struct stmp3xxx_rtc_data { struct rtc_device *rtc; @@ -42,8 +60,8 @@ static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) * NEW_REGS/STALE_REGS bitfields go. In fact it's 0x1=P0, * 0x2=P1, .., 0x20=P5, 0x40=ALARM, 0x80=SECONDS */ - while (__raw_readl(rtc_data->io + HW_RTC_STAT) & - BF(0x80, RTC_STAT_STALE_REGS)) + while (__raw_readl(rtc_data->io + STMP3XXX_RTC_STAT) & + (0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT)) cpu_relax(); } @@ -53,7 +71,8 @@ static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); stmp3xxx_wait_time(rtc_data); - rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_SECONDS), rtc_tm); + rtc_time_to_tm(__raw_readl(rtc_data->io + STMP3XXX_RTC_SECONDS), + rtc_tm); return 0; } @@ -61,7 +80,7 @@ static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t) { struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); - __raw_writel(t, rtc_data->io + HW_RTC_SECONDS); + __raw_writel(t, rtc_data->io + STMP3XXX_RTC_SECONDS); stmp3xxx_wait_time(rtc_data); return 0; } @@ -73,18 +92,19 @@ static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) u32 status; u32 events = 0; - status = __raw_readl(rtc_data->io + HW_RTC_CTRL) & - (BM_RTC_CTRL_ALARM_IRQ | BM_RTC_CTRL_ONEMSEC_IRQ); + status = __raw_readl(rtc_data->io + STMP3XXX_RTC_CTRL) & + (STMP3XXX_RTC_CTRL_ALARM_IRQ | + STMP3XXX_RTC_CTRL_ONEMSEC_IRQ); - if (status & BM_RTC_CTRL_ALARM_IRQ) { - stmp3xxx_clearl(BM_RTC_CTRL_ALARM_IRQ, - rtc_data->io + HW_RTC_CTRL); + if (status & STMP3XXX_RTC_CTRL_ALARM_IRQ) { + stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ALARM_IRQ, + rtc_data->io + STMP3XXX_RTC_CTRL); events |= RTC_AF | RTC_IRQF; } - if (status & BM_RTC_CTRL_ONEMSEC_IRQ) { - stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ, - rtc_data->io + HW_RTC_CTRL); + if (status & STMP3XXX_RTC_CTRL_ONEMSEC_IRQ) { + stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ, + rtc_data->io + STMP3XXX_RTC_CTRL); if (++rtc_data->irq_count % 1000 == 0) { events |= RTC_UF | RTC_IRQF; rtc_data->irq_count = 0; @@ -100,17 +120,17 @@ static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); - void __iomem *p = rtc_data->io + HW_RTC_PERSISTENT0, - *ctl = rtc_data->io + HW_RTC_CTRL; + void __iomem *p = rtc_data->io + STMP3XXX_RTC_PERSISTENT0, + *ctl = rtc_data->io + STMP3XXX_RTC_CTRL; if (enabled) { - stmp3xxx_setl(BM_RTC_PERSISTENT0_ALARM_EN | - BM_RTC_PERSISTENT0_ALARM_WAKE_EN, p); - stmp3xxx_setl(BM_RTC_CTRL_ALARM_IRQ_EN, ctl); + stmp3xxx_setl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, p); + stmp3xxx_setl(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, ctl); } else { - stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | - BM_RTC_PERSISTENT0_ALARM_WAKE_EN, p); - stmp3xxx_clearl(BM_RTC_CTRL_ALARM_IRQ_EN, ctl); + stmp3xxx_clearl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, p); + stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, ctl); } return 0; } @@ -119,7 +139,8 @@ static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) { struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); - rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_ALARM), &alm->time); + rtc_time_to_tm(__raw_readl(rtc_data->io + STMP3XXX_RTC_ALARM), + &alm->time); return 0; } @@ -129,7 +150,7 @@ static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); rtc_tm_to_time(&alm->time, &t); - __raw_writel(t, rtc_data->io + HW_RTC_ALARM); + __raw_writel(t, rtc_data->io + STMP3XXX_RTC_ALARM); return 0; } @@ -149,8 +170,9 @@ static int stmp3xxx_rtc_remove(struct platform_device *pdev) if (!rtc_data) return 0; - stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, - rtc_data->io + HW_RTC_CTRL); + stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | + STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + STMP3XXX_RTC_CTRL); free_irq(rtc_data->irq_alarm, &pdev->dev); free_irq(rtc_data->irq_1msec, &pdev->dev); rtc_device_unregister(rtc_data->rtc); @@ -187,18 +209,18 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) rtc_data->irq_alarm = platform_get_irq(pdev, 0); rtc_data->irq_1msec = platform_get_irq(pdev, 1); - if (!(__raw_readl(HW_RTC_STAT + rtc_data->io) & - BM_RTC_STAT_RTC_PRESENT)) { + if (!(__raw_readl(STMP3XXX_RTC_STAT + rtc_data->io) & + STMP3XXX_RTC_STAT_RTC_PRESENT)) { dev_err(&pdev->dev, "no device onboard\n"); err = -ENODEV; goto out_remap; } stmp3xxx_reset_block(rtc_data->io, true); - stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | - BM_RTC_PERSISTENT0_ALARM_WAKE_EN | - BM_RTC_PERSISTENT0_ALARM_WAKE, - rtc_data->io + HW_RTC_PERSISTENT0); + stmp3xxx_clearl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, + rtc_data->io + STMP3XXX_RTC_PERSISTENT0); rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, &stmp3xxx_rtc_ops, THIS_MODULE); if (IS_ERR(rtc_data->rtc)) { @@ -229,8 +251,9 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) out_irq1: free_irq(rtc_data->irq_alarm, &pdev->dev); out_irq_alarm: - stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, - rtc_data->io + HW_RTC_CTRL); + stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | + STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + STMP3XXX_RTC_CTRL); rtc_device_unregister(rtc_data->rtc); out_remap: iounmap(rtc_data->io); @@ -250,10 +273,10 @@ static int stmp3xxx_rtc_resume(struct platform_device *dev) struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); stmp3xxx_reset_block(rtc_data->io, true); - stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | - BM_RTC_PERSISTENT0_ALARM_WAKE_EN | - BM_RTC_PERSISTENT0_ALARM_WAKE, - rtc_data->io + HW_RTC_PERSISTENT0); + stmp3xxx_clearl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, + rtc_data->io + STMP3XXX_RTC_PERSISTENT0); return 0; } #else -- cgit v1.2.3 From 46b21218145ecef0936eb4338a9e0ffef84f00cf Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 25 May 2011 12:56:50 +0200 Subject: rtc: stmp3xxx: Port stmp-functions to mxs-equivalents The stmp3xxx driver used to include functions from a stmp-specific include. Because of consolidation, plat-stmp has now been removed and merged with the compatible mach-mxs. Use the apropriate mxs-functions for transition. The accessors will be converted to readl/writel in a later patch. Signed-off-by: Wolfram Sang Tested-by: Shawn Guo Signed-off-by: John Stultz --- drivers/rtc/Kconfig | 6 +++--- drivers/rtc/rtc-stmp3xxx.c | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index ce2aabf5c550..dcb61e23b985 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -981,11 +981,11 @@ config RTC_DRV_COH901331 config RTC_DRV_STMP - tristate "Freescale STMP3xxx RTC" - depends on ARCH_STMP3XXX + tristate "Freescale STMP3xxx/i.MX23/i.MX28 RTC" + depends on ARCH_MXS help If you say yes here you will get support for the onboard - STMP3xxx RTC. + STMP3xxx/i.MX23/i.MX28 RTC. This driver can also be built as a module. If so, the module will be called rtc-stmp3xxx. diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index e54bea076067..ac40f971a40e 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -24,8 +24,8 @@ #include #include -#include -#include +#include +#include #define STMP3XXX_RTC_CTRL 0x0 #define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN 0x00000001 @@ -97,13 +97,13 @@ static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) STMP3XXX_RTC_CTRL_ONEMSEC_IRQ); if (status & STMP3XXX_RTC_CTRL_ALARM_IRQ) { - stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ALARM_IRQ, + __mxs_clrl(STMP3XXX_RTC_CTRL_ALARM_IRQ, rtc_data->io + STMP3XXX_RTC_CTRL); events |= RTC_AF | RTC_IRQF; } if (status & STMP3XXX_RTC_CTRL_ONEMSEC_IRQ) { - stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ, + __mxs_clrl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ, rtc_data->io + STMP3XXX_RTC_CTRL); if (++rtc_data->irq_count % 1000 == 0) { events |= RTC_UF | RTC_IRQF; @@ -124,13 +124,13 @@ static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) *ctl = rtc_data->io + STMP3XXX_RTC_CTRL; if (enabled) { - stmp3xxx_setl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + __mxs_setl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, p); - stmp3xxx_setl(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, ctl); + __mxs_setl(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, ctl); } else { - stmp3xxx_clearl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + __mxs_clrl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, p); - stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, ctl); + __mxs_clrl(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, ctl); } return 0; } @@ -170,7 +170,7 @@ static int stmp3xxx_rtc_remove(struct platform_device *pdev) if (!rtc_data) return 0; - stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | + __mxs_clrl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, rtc_data->io + STMP3XXX_RTC_CTRL); free_irq(rtc_data->irq_alarm, &pdev->dev); @@ -216,8 +216,8 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) goto out_remap; } - stmp3xxx_reset_block(rtc_data->io, true); - stmp3xxx_clearl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + mxs_reset_block(rtc_data->io); + __mxs_clrl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, rtc_data->io + STMP3XXX_RTC_PERSISTENT0); @@ -251,7 +251,7 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) out_irq1: free_irq(rtc_data->irq_alarm, &pdev->dev); out_irq_alarm: - stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | + __mxs_clrl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, rtc_data->io + STMP3XXX_RTC_CTRL); rtc_device_unregister(rtc_data->rtc); @@ -272,8 +272,8 @@ static int stmp3xxx_rtc_resume(struct platform_device *dev) { struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); - stmp3xxx_reset_block(rtc_data->io, true); - stmp3xxx_clearl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + mxs_reset_block(rtc_data->io); + __mxs_clrl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, rtc_data->io + STMP3XXX_RTC_PERSISTENT0); -- cgit v1.2.3 From a91d2bab355f3a5caa767d7316f80422bfcd2ad6 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 25 May 2011 12:56:51 +0200 Subject: rtc: stmp3xxx: Initialize drvdata before registering device Commit f44f7f96a20 ("RTC: Initialize kernel state from RTC") uncovered an issue in a number of RTC drivers, where the drivers call rtc_device_register before initializing the device or platform drvdata. This frequently results in null pointer dereferences when the rtc_device_register immediately makes use of the rtc device, calling rtc_read_alarm. The solution is to ensure the drvdata is initialized prior to registering the rtc device. Signed-off-by: Wolfram Sang Tested-by: Shawn Guo Signed-off-by: John Stultz --- drivers/rtc/rtc-stmp3xxx.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index ac40f971a40e..90f05fe60620 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -176,6 +176,7 @@ static int stmp3xxx_rtc_remove(struct platform_device *pdev) free_irq(rtc_data->irq_alarm, &pdev->dev); free_irq(rtc_data->irq_1msec, &pdev->dev); rtc_device_unregister(rtc_data->rtc); + platform_set_drvdata(pdev, NULL); iounmap(rtc_data->io); kfree(rtc_data); @@ -216,11 +217,14 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) goto out_remap; } + platform_set_drvdata(pdev, rtc_data); + mxs_reset_block(rtc_data->io); __mxs_clrl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, rtc_data->io + STMP3XXX_RTC_PERSISTENT0); + rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, &stmp3xxx_rtc_ops, THIS_MODULE); if (IS_ERR(rtc_data->rtc)) { @@ -244,8 +248,6 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) goto out_irq1; } - platform_set_drvdata(pdev, rtc_data); - return 0; out_irq1: @@ -256,6 +258,7 @@ out_irq_alarm: rtc_data->io + STMP3XXX_RTC_CTRL); rtc_device_unregister(rtc_data->rtc); out_remap: + platform_set_drvdata(pdev, NULL); iounmap(rtc_data->io); out_free: kfree(rtc_data); -- cgit v1.2.3 From b5167159d4341d408ef6b941975f2fe8973b2a94 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 25 May 2011 12:56:52 +0200 Subject: rtc: stmp3xxx: Get rid of mach-specific accessors Replace the accessors with standard readl/writel to remove their platform-dependency. Also, drop __raw_(read|write)l-accessors while we are here. Signed-off-by: Wolfram Sang Tested-by: Shawn Guo Signed-off-by: John Stultz --- drivers/rtc/rtc-stmp3xxx.c | 64 ++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 30 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index 90f05fe60620..ad1851796940 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -18,6 +18,7 @@ */ #include #include +#include #include #include #include @@ -25,9 +26,10 @@ #include #include -#include #define STMP3XXX_RTC_CTRL 0x0 +#define STMP3XXX_RTC_CTRL_SET 0x4 +#define STMP3XXX_RTC_CTRL_CLR 0x8 #define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN 0x00000001 #define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002 #define STMP3XXX_RTC_CTRL_ALARM_IRQ 0x00000004 @@ -42,6 +44,8 @@ #define STMP3XXX_RTC_ALARM 0x40 #define STMP3XXX_RTC_PERSISTENT0 0x60 +#define STMP3XXX_RTC_PERSISTENT0_SET 0x64 +#define STMP3XXX_RTC_PERSISTENT0_CLR 0x68 #define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN 0x00000002 #define STMP3XXX_RTC_PERSISTENT0_ALARM_EN 0x00000004 #define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE 0x00000080 @@ -60,7 +64,7 @@ static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) * NEW_REGS/STALE_REGS bitfields go. In fact it's 0x1=P0, * 0x2=P1, .., 0x20=P5, 0x40=ALARM, 0x80=SECONDS */ - while (__raw_readl(rtc_data->io + STMP3XXX_RTC_STAT) & + while (readl(rtc_data->io + STMP3XXX_RTC_STAT) & (0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT)) cpu_relax(); } @@ -71,8 +75,7 @@ static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); stmp3xxx_wait_time(rtc_data); - rtc_time_to_tm(__raw_readl(rtc_data->io + STMP3XXX_RTC_SECONDS), - rtc_tm); + rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_SECONDS), rtc_tm); return 0; } @@ -80,7 +83,7 @@ static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t) { struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); - __raw_writel(t, rtc_data->io + STMP3XXX_RTC_SECONDS); + writel(t, rtc_data->io + STMP3XXX_RTC_SECONDS); stmp3xxx_wait_time(rtc_data); return 0; } @@ -92,19 +95,19 @@ static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) u32 status; u32 events = 0; - status = __raw_readl(rtc_data->io + STMP3XXX_RTC_CTRL) & + status = readl(rtc_data->io + STMP3XXX_RTC_CTRL) & (STMP3XXX_RTC_CTRL_ALARM_IRQ | STMP3XXX_RTC_CTRL_ONEMSEC_IRQ); if (status & STMP3XXX_RTC_CTRL_ALARM_IRQ) { - __mxs_clrl(STMP3XXX_RTC_CTRL_ALARM_IRQ, - rtc_data->io + STMP3XXX_RTC_CTRL); + writel(STMP3XXX_RTC_CTRL_ALARM_IRQ, + rtc_data->io + STMP3XXX_RTC_CTRL_CLR); events |= RTC_AF | RTC_IRQF; } if (status & STMP3XXX_RTC_CTRL_ONEMSEC_IRQ) { - __mxs_clrl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ, - rtc_data->io + STMP3XXX_RTC_CTRL); + writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ, + rtc_data->io + STMP3XXX_RTC_CTRL_CLR); if (++rtc_data->irq_count % 1000 == 0) { events |= RTC_UF | RTC_IRQF; rtc_data->irq_count = 0; @@ -120,17 +123,19 @@ static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); - void __iomem *p = rtc_data->io + STMP3XXX_RTC_PERSISTENT0, - *ctl = rtc_data->io + STMP3XXX_RTC_CTRL; if (enabled) { - __mxs_setl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | - STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, p); - __mxs_setl(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, ctl); + writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, + rtc_data->io + STMP3XXX_RTC_PERSISTENT0_SET); + writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + STMP3XXX_RTC_CTRL_SET); } else { - __mxs_clrl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | - STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, p); - __mxs_clrl(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, ctl); + writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, + rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR); + writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + STMP3XXX_RTC_CTRL_CLR); } return 0; } @@ -139,8 +144,7 @@ static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) { struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); - rtc_time_to_tm(__raw_readl(rtc_data->io + STMP3XXX_RTC_ALARM), - &alm->time); + rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_ALARM), &alm->time); return 0; } @@ -150,7 +154,7 @@ static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); rtc_tm_to_time(&alm->time, &t); - __raw_writel(t, rtc_data->io + STMP3XXX_RTC_ALARM); + writel(t, rtc_data->io + STMP3XXX_RTC_ALARM); return 0; } @@ -170,9 +174,9 @@ static int stmp3xxx_rtc_remove(struct platform_device *pdev) if (!rtc_data) return 0; - __mxs_clrl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | + writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, - rtc_data->io + STMP3XXX_RTC_CTRL); + rtc_data->io + STMP3XXX_RTC_CTRL_CLR); free_irq(rtc_data->irq_alarm, &pdev->dev); free_irq(rtc_data->irq_1msec, &pdev->dev); rtc_device_unregister(rtc_data->rtc); @@ -210,7 +214,7 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) rtc_data->irq_alarm = platform_get_irq(pdev, 0); rtc_data->irq_1msec = platform_get_irq(pdev, 1); - if (!(__raw_readl(STMP3XXX_RTC_STAT + rtc_data->io) & + if (!(readl(STMP3XXX_RTC_STAT + rtc_data->io) & STMP3XXX_RTC_STAT_RTC_PRESENT)) { dev_err(&pdev->dev, "no device onboard\n"); err = -ENODEV; @@ -220,10 +224,10 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rtc_data); mxs_reset_block(rtc_data->io); - __mxs_clrl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, - rtc_data->io + STMP3XXX_RTC_PERSISTENT0); + rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR); rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, &stmp3xxx_rtc_ops, THIS_MODULE); @@ -253,9 +257,9 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) out_irq1: free_irq(rtc_data->irq_alarm, &pdev->dev); out_irq_alarm: - __mxs_clrl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | + writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, - rtc_data->io + STMP3XXX_RTC_CTRL); + rtc_data->io + STMP3XXX_RTC_CTRL_CLR); rtc_device_unregister(rtc_data->rtc); out_remap: platform_set_drvdata(pdev, NULL); @@ -276,10 +280,10 @@ static int stmp3xxx_rtc_resume(struct platform_device *dev) struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); mxs_reset_block(rtc_data->io); - __mxs_clrl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, - rtc_data->io + STMP3XXX_RTC_PERSISTENT0); + rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR); return 0; } #else -- cgit v1.2.3 From 7e794cb7e36ccdb8c44b8ca7738720625b7aa2da Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 25 May 2011 12:56:53 +0200 Subject: rtc: stmp3xxx: Remove UIE handlers The RTC core handles UIE since 6610e08 (RTC: Rework RTC code to use timerqueue for events), so remove the specific interrupt in this driver. To make it work at all, enable interrupts in set_alarm() if needed. Drop IRQF_DISABLED which is deprecated, while we are here. Finally, add my copyright after all these changes. Signed-off-by: Wolfram Sang Tested-by: Shawn Guo Signed-off-by: John Stultz --- drivers/rtc/rtc-stmp3xxx.c | 61 ++++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 43 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index ad1851796940..7315068daa59 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -6,6 +6,7 @@ * * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * Copyright 2011 Wolfram Sang, Pengutronix e.K. */ /* @@ -33,7 +34,6 @@ #define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN 0x00000001 #define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002 #define STMP3XXX_RTC_CTRL_ALARM_IRQ 0x00000004 -#define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ 0x00000008 #define STMP3XXX_RTC_STAT 0x10 #define STMP3XXX_RTC_STAT_STALE_SHIFT 16 @@ -52,9 +52,8 @@ struct stmp3xxx_rtc_data { struct rtc_device *rtc; - unsigned irq_count; void __iomem *io; - int irq_alarm, irq_1msec; + int irq_alarm; }; static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) @@ -92,32 +91,16 @@ static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t) static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) { struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev_id); - u32 status; - u32 events = 0; - - status = readl(rtc_data->io + STMP3XXX_RTC_CTRL) & - (STMP3XXX_RTC_CTRL_ALARM_IRQ | - STMP3XXX_RTC_CTRL_ONEMSEC_IRQ); + u32 status = readl(rtc_data->io + STMP3XXX_RTC_CTRL); if (status & STMP3XXX_RTC_CTRL_ALARM_IRQ) { writel(STMP3XXX_RTC_CTRL_ALARM_IRQ, rtc_data->io + STMP3XXX_RTC_CTRL_CLR); - events |= RTC_AF | RTC_IRQF; + rtc_update_irq(rtc_data->rtc, 1, RTC_AF | RTC_IRQF); + return IRQ_HANDLED; } - if (status & STMP3XXX_RTC_CTRL_ONEMSEC_IRQ) { - writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ, - rtc_data->io + STMP3XXX_RTC_CTRL_CLR); - if (++rtc_data->irq_count % 1000 == 0) { - events |= RTC_UF | RTC_IRQF; - rtc_data->irq_count = 0; - } - } - - if (events) - rtc_update_irq(rtc_data->rtc, 1, events); - - return IRQ_HANDLED; + return IRQ_NONE; } static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) @@ -155,6 +138,9 @@ static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) rtc_tm_to_time(&alm->time, &t); writel(t, rtc_data->io + STMP3XXX_RTC_ALARM); + + stmp3xxx_alarm_irq_enable(dev, alm->enabled); + return 0; } @@ -174,11 +160,9 @@ static int stmp3xxx_rtc_remove(struct platform_device *pdev) if (!rtc_data) return 0; - writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | - STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, rtc_data->io + STMP3XXX_RTC_CTRL_CLR); free_irq(rtc_data->irq_alarm, &pdev->dev); - free_irq(rtc_data->irq_1msec, &pdev->dev); rtc_device_unregister(rtc_data->rtc); platform_set_drvdata(pdev, NULL); iounmap(rtc_data->io); @@ -212,7 +196,6 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) } rtc_data->irq_alarm = platform_get_irq(pdev, 0); - rtc_data->irq_1msec = platform_get_irq(pdev, 1); if (!(readl(STMP3XXX_RTC_STAT + rtc_data->io) & STMP3XXX_RTC_STAT_RTC_PRESENT)) { @@ -229,6 +212,10 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR); + writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | + STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + STMP3XXX_RTC_CTRL_CLR); + rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, &stmp3xxx_rtc_ops, THIS_MODULE); if (IS_ERR(rtc_data->rtc)) { @@ -236,30 +223,17 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) goto out_remap; } - rtc_data->irq_count = 0; - err = request_irq(rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, - IRQF_DISABLED, "RTC alarm", &pdev->dev); + err = request_irq(rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, 0, + "RTC alarm", &pdev->dev); if (err) { dev_err(&pdev->dev, "Cannot claim IRQ%d\n", rtc_data->irq_alarm); goto out_irq_alarm; } - err = request_irq(rtc_data->irq_1msec, stmp3xxx_rtc_interrupt, - IRQF_DISABLED, "RTC tick", &pdev->dev); - if (err) { - dev_err(&pdev->dev, "Cannot claim IRQ%d\n", - rtc_data->irq_1msec); - goto out_irq1; - } return 0; -out_irq1: - free_irq(rtc_data->irq_alarm, &pdev->dev); out_irq_alarm: - writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | - STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, - rtc_data->io + STMP3XXX_RTC_CTRL_CLR); rtc_device_unregister(rtc_data->rtc); out_remap: platform_set_drvdata(pdev, NULL); @@ -316,5 +290,6 @@ module_init(stmp3xxx_rtc_init); module_exit(stmp3xxx_rtc_exit); MODULE_DESCRIPTION("STMP3xxx RTC Driver"); -MODULE_AUTHOR("dmitry pervushin "); +MODULE_AUTHOR("dmitry pervushin and " + "Wolfram Sang "); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 955dbea3c7133d3ccfaa79c7eba1244c1de42865 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Mon, 25 Jul 2011 17:13:30 -0700 Subject: drivers/rtc/rtc-mpc5121.c: add support for RTC on MPC5200 MPC5200B contains a limited version of RTC from MPC5121. Add support for the RTC on that CPU. Signed-off-by: Dmitry Eremin-Solenikov Cc: Alessandro Zummo Cc: Anatolij Gustschin Cc: Grant Likely Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 4 +-- drivers/rtc/rtc-mpc5121.c | 81 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 10 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index dcb61e23b985..55affcdc4641 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1006,10 +1006,10 @@ config RTC_DRV_MC13XXX config RTC_DRV_MPC5121 tristate "Freescale MPC5121 built-in RTC" - depends on PPC_MPC512x && RTC_CLASS + depends on PPC_MPC512x || PPC_MPC52xx help If you say yes here you will get support for the - built-in RTC MPC5121. + built-in RTC on MPC5121 or on MPC5200. This driver can also be built as a module. If so, the module will be called rtc-mpc5121. diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index 09ccd8d3ba2a..da60915818b6 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -3,6 +3,7 @@ * * Copyright 2007, Domen Puncer * Copyright 2008, Freescale Semiconductor, Inc. All rights reserved. + * Copyright 2011, Dmitry Eremin-Solenikov * * 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 @@ -145,6 +146,55 @@ static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm) return 0; } +static int mpc5200_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); + struct mpc5121_rtc_regs __iomem *regs = rtc->regs; + int tmp; + + tm->tm_sec = in_8(®s->second); + tm->tm_min = in_8(®s->minute); + + /* 12 hour format? */ + if (in_8(®s->hour) & 0x20) + tm->tm_hour = (in_8(®s->hour) >> 1) + + (in_8(®s->hour) & 1 ? 12 : 0); + else + tm->tm_hour = in_8(®s->hour); + + tmp = in_8(®s->wday_mday); + tm->tm_mday = tmp & 0x1f; + tm->tm_mon = in_8(®s->month) - 1; + tm->tm_year = in_be16(®s->year) - 1900; + tm->tm_wday = (tmp >> 5) % 7; + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); + tm->tm_isdst = 0; + + return 0; +} + +static int mpc5200_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); + struct mpc5121_rtc_regs __iomem *regs = rtc->regs; + + mpc5121_rtc_update_smh(regs, tm); + + /* date */ + out_8(®s->month_set, tm->tm_mon + 1); + out_8(®s->weekday_set, tm->tm_wday ? tm->tm_wday : 7); + out_8(®s->date_set, tm->tm_mday); + out_be16(®s->year_set, tm->tm_year + 1900); + + /* set date sequence */ + out_8(®s->set_date, 0x1); + out_8(®s->set_date, 0x3); + out_8(®s->set_date, 0x1); + out_8(®s->set_date, 0x0); + + return 0; +} + static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) { struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); @@ -248,11 +298,18 @@ static const struct rtc_class_ops mpc5121_rtc_ops = { .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable, }; +static const struct rtc_class_ops mpc5200_rtc_ops = { + .read_time = mpc5200_rtc_read_time, + .set_time = mpc5200_rtc_set_time, + .read_alarm = mpc5121_rtc_read_alarm, + .set_alarm = mpc5121_rtc_set_alarm, + .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable, +}; + static int __devinit mpc5121_rtc_probe(struct platform_device *op) { struct mpc5121_rtc_data *rtc; int err = 0; - u32 ka; rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); if (!rtc) @@ -287,15 +344,22 @@ static int __devinit mpc5121_rtc_probe(struct platform_device *op) goto out_dispose2; } - ka = in_be32(&rtc->regs->keep_alive); - if (ka & 0x02) { - dev_warn(&op->dev, - "mpc5121-rtc: Battery or oscillator failure!\n"); - out_be32(&rtc->regs->keep_alive, ka); + if (of_device_is_compatible(op->dev.of_node, "fsl,mpc5121-rtc")) { + u32 ka; + ka = in_be32(&rtc->regs->keep_alive); + if (ka & 0x02) { + dev_warn(&op->dev, + "mpc5121-rtc: Battery or oscillator failure!\n"); + out_be32(&rtc->regs->keep_alive, ka); + } + + rtc->rtc = rtc_device_register("mpc5121-rtc", &op->dev, + &mpc5121_rtc_ops, THIS_MODULE); + } else { + rtc->rtc = rtc_device_register("mpc5200-rtc", &op->dev, + &mpc5200_rtc_ops, THIS_MODULE); } - rtc->rtc = rtc_device_register("mpc5121-rtc", &op->dev, - &mpc5121_rtc_ops, THIS_MODULE); if (IS_ERR(rtc->rtc)) { err = PTR_ERR(rtc->rtc); goto out_free_irq; @@ -340,6 +404,7 @@ static int __devexit mpc5121_rtc_remove(struct platform_device *op) static struct of_device_id mpc5121_rtc_match[] __devinitdata = { { .compatible = "fsl,mpc5121-rtc", }, + { .compatible = "fsl,mpc5200-rtc", }, {}, }; -- cgit v1.2.3 From cefe4fbbaab8e20a7581a187db82d33c1e3320c0 Mon Sep 17 00:00:00 2001 From: Donggeun Kim Date: Mon, 25 Jul 2011 17:13:32 -0700 Subject: drivers/rtc/rtc-s3c.c: support clock gating Add support for clock gating. Power consumption can be reduced by setting rtc_clk disabled state except for when RTC related registers are accessed. Signed-off-by: Donggeun Kim Signed-off-by: MyungJoo Ham Signed-off-by: KyungMin Park Cc: Alessandro Zummo Cc: Ben Dooks Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-s3c.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 16512ecae31a..53c99b1a3c92 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -57,11 +57,13 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) { struct rtc_device *rdev = id; + clk_enable(rtc_clk); rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); if (s3c_rtc_cpu_type == TYPE_S3C64XX) writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP); + clk_disable(rtc_clk); return IRQ_HANDLED; } @@ -69,11 +71,13 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id) { struct rtc_device *rdev = id; + clk_enable(rtc_clk); rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); if (s3c_rtc_cpu_type == TYPE_S3C64XX) writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP); + clk_disable(rtc_clk); return IRQ_HANDLED; } @@ -84,12 +88,14 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) pr_debug("%s: aie=%d\n", __func__, enabled); + clk_enable(rtc_clk); tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; if (enabled) tmp |= S3C2410_RTCALM_ALMEN; writeb(tmp, s3c_rtc_base + S3C2410_RTCALM); + clk_disable(rtc_clk); return 0; } @@ -103,6 +109,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq) if (!is_power_of_2(freq)) return -EINVAL; + clk_enable(rtc_clk); spin_lock_irq(&s3c_rtc_pie_lock); if (s3c_rtc_cpu_type == TYPE_S3C2410) { @@ -114,6 +121,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq) writel(tmp, s3c_rtc_base + S3C2410_TICNT); spin_unlock_irq(&s3c_rtc_pie_lock); + clk_disable(rtc_clk); return 0; } @@ -125,6 +133,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) unsigned int have_retried = 0; void __iomem *base = s3c_rtc_base; + clk_enable(rtc_clk); retry_get_time: rtc_tm->tm_min = readb(base + S3C2410_RTCMIN); rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR); @@ -157,6 +166,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) rtc_tm->tm_year += 100; rtc_tm->tm_mon -= 1; + clk_disable(rtc_clk); return rtc_valid_tm(rtc_tm); } @@ -165,6 +175,7 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) void __iomem *base = s3c_rtc_base; int year = tm->tm_year - 100; + clk_enable(rtc_clk); pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n", 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); @@ -182,6 +193,7 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE); writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON); writeb(bin2bcd(year), base + S3C2410_RTCYEAR); + clk_disable(rtc_clk); return 0; } @@ -192,6 +204,7 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) void __iomem *base = s3c_rtc_base; unsigned int alm_en; + clk_enable(rtc_clk); alm_tm->tm_sec = readb(base + S3C2410_ALMSEC); alm_tm->tm_min = readb(base + S3C2410_ALMMIN); alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR); @@ -243,6 +256,7 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) else alm_tm->tm_year = -1; + clk_disable(rtc_clk); return 0; } @@ -252,6 +266,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) void __iomem *base = s3c_rtc_base; unsigned int alrm_en; + clk_enable(rtc_clk); pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", alrm->enabled, 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, @@ -282,6 +297,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) s3c_rtc_setaie(dev, alrm->enabled); + clk_disable(rtc_clk); return 0; } @@ -289,6 +305,7 @@ static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) { unsigned int ticnt; + clk_enable(rtc_clk); if (s3c_rtc_cpu_type == TYPE_S3C64XX) { ticnt = readw(s3c_rtc_base + S3C2410_RTCCON); ticnt &= S3C64XX_RTCCON_TICEN; @@ -298,6 +315,7 @@ static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) } seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); + clk_disable(rtc_clk); return 0; } @@ -360,6 +378,7 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en) if (s3c_rtc_base == NULL) return; + clk_enable(rtc_clk); if (!en) { tmp = readw(base + S3C2410_RTCCON); if (s3c_rtc_cpu_type == TYPE_S3C64XX) @@ -399,6 +418,7 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en) base + S3C2410_RTCCON); } } + clk_disable(rtc_clk); } static int __devexit s3c_rtc_remove(struct platform_device *dev) @@ -410,7 +430,6 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev) s3c_rtc_setaie(&dev->dev, 0); - clk_disable(rtc_clk); clk_put(rtc_clk); rtc_clk = NULL; @@ -530,6 +549,8 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) s3c_rtc_setfreq(&pdev->dev, 1); + clk_disable(rtc_clk); + return 0; err_nortc: @@ -555,6 +576,7 @@ static int ticnt_save, ticnt_en_save; static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) { + clk_enable(rtc_clk); /* save TICNT for anyone using periodic interrupts */ ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); if (s3c_rtc_cpu_type == TYPE_S3C64XX) { @@ -569,6 +591,7 @@ static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) else dev_err(&pdev->dev, "enable_irq_wake failed\n"); } + clk_disable(rtc_clk); return 0; } @@ -577,6 +600,7 @@ static int s3c_rtc_resume(struct platform_device *pdev) { unsigned int tmp; + clk_enable(rtc_clk); s3c_rtc_enable(pdev, 1); writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) { @@ -588,6 +612,7 @@ static int s3c_rtc_resume(struct platform_device *pdev) disable_irq_wake(s3c_rtc_alarmno); wake_en = false; } + clk_disable(rtc_clk); return 0; } -- cgit v1.2.3 From 9a9a54ad7aa2c7420c96c6fd33538f55d81775cb Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Mon, 25 Jul 2011 17:13:33 -0700 Subject: drivers/rtc: add support for Qualcomm PMIC8xxx RTC Add support for PMIC8xxx based RTC. PMIC8xxx is Qualcomm's power management IC that internally houses an RTC module. This driver communicates with the PMIC module over SSBI bus. [akpm@linux-foundation.org: cosmetic tweaks] Acked-by: Wan ZongShun Reviewed-by: Stephen Boyd Signed-off-by: Anirudh Ghayal Signed-off-by: Ashay Jaiswal Cc: Samuel Ortiz Cc: Wan ZongShun Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-pm8xxx.c | 550 +++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/pm8xxx/rtc.h | 25 ++ 4 files changed, 586 insertions(+) create mode 100644 drivers/rtc/rtc-pm8xxx.c create mode 100644 include/linux/mfd/pm8xxx/rtc.h (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 55affcdc4641..5a538fc1cc85 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1034,6 +1034,16 @@ config RTC_DRV_LPC32XX This driver can also be buillt as a module. If so, the module will be called rtc-lpc32xx. +config RTC_DRV_PM8XXX + tristate "Qualcomm PMIC8XXX RTC" + depends on MFD_PM8XXX + help + If you say yes here you get support for the + Qualcomm PMIC8XXX RTC. + + To compile this driver as a module, choose M here: the + module will be called rtc-pm8xxx. + config RTC_DRV_TEGRA tristate "NVIDIA Tegra Internal RTC driver" depends on RTC_CLASS && ARCH_TEGRA diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 0ffefe877bfa..6e6982335c10 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o +obj-$(CONFIG_RTC_DRV_PM8XXX) += rtc-pm8xxx.o obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c new file mode 100644 index 000000000000..d420e9d877e8 --- /dev/null +++ b/drivers/rtc/rtc-pm8xxx.c @@ -0,0 +1,550 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* RTC Register offsets from RTC CTRL REG */ +#define PM8XXX_ALARM_CTRL_OFFSET 0x01 +#define PM8XXX_RTC_WRITE_OFFSET 0x02 +#define PM8XXX_RTC_READ_OFFSET 0x06 +#define PM8XXX_ALARM_RW_OFFSET 0x0A + +/* RTC_CTRL register bit fields */ +#define PM8xxx_RTC_ENABLE BIT(7) +#define PM8xxx_RTC_ALARM_ENABLE BIT(1) +#define PM8xxx_RTC_ALARM_CLEAR BIT(0) + +#define NUM_8_BIT_RTC_REGS 0x4 + +/** + * struct pm8xxx_rtc - rtc driver internal structure + * @rtc: rtc device for this driver. + * @rtc_alarm_irq: rtc alarm irq number. + * @rtc_base: address of rtc control register. + * @rtc_read_base: base address of read registers. + * @rtc_write_base: base address of write registers. + * @alarm_rw_base: base address of alarm registers. + * @ctrl_reg: rtc control register. + * @rtc_dev: device structure. + * @ctrl_reg_lock: spinlock protecting access to ctrl_reg. + */ +struct pm8xxx_rtc { + struct rtc_device *rtc; + int rtc_alarm_irq; + int rtc_base; + int rtc_read_base; + int rtc_write_base; + int alarm_rw_base; + u8 ctrl_reg; + struct device *rtc_dev; + spinlock_t ctrl_reg_lock; +}; + +/* + * The RTC registers need to be read/written one byte at a time. This is a + * hardware limitation. + */ +static int pm8xxx_read_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val, + int base, int count) +{ + int i, rc; + struct device *parent = rtc_dd->rtc_dev->parent; + + for (i = 0; i < count; i++) { + rc = pm8xxx_readb(parent, base + i, &rtc_val[i]); + if (rc < 0) { + dev_err(rtc_dd->rtc_dev, "PMIC read failed\n"); + return rc; + } + } + + return 0; +} + +static int pm8xxx_write_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val, + int base, int count) +{ + int i, rc; + struct device *parent = rtc_dd->rtc_dev->parent; + + for (i = 0; i < count; i++) { + rc = pm8xxx_writeb(parent, base + i, rtc_val[i]); + if (rc < 0) { + dev_err(rtc_dd->rtc_dev, "PMIC write failed\n"); + return rc; + } + } + + return 0; +} + +/* + * Steps to write the RTC registers. + * 1. Disable alarm if enabled. + * 2. Write 0x00 to LSB. + * 3. Write Byte[1], Byte[2], Byte[3] then Byte[0]. + * 4. Enable alarm if disabled in step 1. + */ +static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + int rc, i; + unsigned long secs, irq_flags; + u8 value[NUM_8_BIT_RTC_REGS], reg = 0, alarm_enabled = 0, ctrl_reg; + struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + + rtc_tm_to_time(tm, &secs); + + for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) { + value[i] = secs & 0xFF; + secs >>= 8; + } + + dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs); + + spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); + ctrl_reg = rtc_dd->ctrl_reg; + + if (ctrl_reg & PM8xxx_RTC_ALARM_ENABLE) { + alarm_enabled = 1; + ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE; + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, + 1); + if (rc < 0) { + dev_err(dev, "Write to RTC control register " + "failed\n"); + goto rtc_rw_fail; + } + rtc_dd->ctrl_reg = ctrl_reg; + } else + spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); + + /* Write 0 to Byte[0] */ + reg = 0; + rc = pm8xxx_write_wrapper(rtc_dd, ®, rtc_dd->rtc_write_base, 1); + if (rc < 0) { + dev_err(dev, "Write to RTC write data register failed\n"); + goto rtc_rw_fail; + } + + /* Write Byte[1], Byte[2], Byte[3] */ + rc = pm8xxx_write_wrapper(rtc_dd, value + 1, + rtc_dd->rtc_write_base + 1, 3); + if (rc < 0) { + dev_err(dev, "Write to RTC write data register failed\n"); + goto rtc_rw_fail; + } + + /* Write Byte[0] */ + rc = pm8xxx_write_wrapper(rtc_dd, value, rtc_dd->rtc_write_base, 1); + if (rc < 0) { + dev_err(dev, "Write to RTC write data register failed\n"); + goto rtc_rw_fail; + } + + if (alarm_enabled) { + ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE; + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, + 1); + if (rc < 0) { + dev_err(dev, "Write to RTC control register " + "failed\n"); + goto rtc_rw_fail; + } + rtc_dd->ctrl_reg = ctrl_reg; + } + +rtc_rw_fail: + if (alarm_enabled) + spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); + + return rc; +} + +static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + int rc; + u8 value[NUM_8_BIT_RTC_REGS], reg; + unsigned long secs; + struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + + rc = pm8xxx_read_wrapper(rtc_dd, value, rtc_dd->rtc_read_base, + NUM_8_BIT_RTC_REGS); + if (rc < 0) { + dev_err(dev, "RTC read data register failed\n"); + return rc; + } + + /* + * Read the LSB again and check if there has been a carry over. + * If there is, redo the read operation. + */ + rc = pm8xxx_read_wrapper(rtc_dd, ®, rtc_dd->rtc_read_base, 1); + if (rc < 0) { + dev_err(dev, "RTC read data register failed\n"); + return rc; + } + + if (unlikely(reg < value[0])) { + rc = pm8xxx_read_wrapper(rtc_dd, value, + rtc_dd->rtc_read_base, NUM_8_BIT_RTC_REGS); + if (rc < 0) { + dev_err(dev, "RTC read data register failed\n"); + return rc; + } + } + + secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); + + rtc_time_to_tm(secs, tm); + + rc = rtc_valid_tm(tm); + if (rc < 0) { + dev_err(dev, "Invalid time read from RTC\n"); + return rc; + } + + dev_dbg(dev, "secs = %lu, h:m:s == %d:%d:%d, d/m/y = %d/%d/%d\n", + secs, tm->tm_hour, tm->tm_min, tm->tm_sec, + tm->tm_mday, tm->tm_mon, tm->tm_year); + + return 0; +} + +static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + int rc, i; + u8 value[NUM_8_BIT_RTC_REGS], ctrl_reg; + unsigned long secs, irq_flags; + struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + + rtc_tm_to_time(&alarm->time, &secs); + + for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) { + value[i] = secs & 0xFF; + secs >>= 8; + } + + spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); + + rc = pm8xxx_write_wrapper(rtc_dd, value, rtc_dd->alarm_rw_base, + NUM_8_BIT_RTC_REGS); + if (rc < 0) { + dev_err(dev, "Write to RTC ALARM register failed\n"); + goto rtc_rw_fail; + } + + ctrl_reg = rtc_dd->ctrl_reg; + ctrl_reg = alarm->enabled ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) : + (ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE); + + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); + if (rc < 0) { + dev_err(dev, "Write to RTC control register failed\n"); + goto rtc_rw_fail; + } + + rtc_dd->ctrl_reg = ctrl_reg; + + dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n", + alarm->time.tm_hour, alarm->time.tm_min, + alarm->time.tm_sec, alarm->time.tm_mday, + alarm->time.tm_mon, alarm->time.tm_year); +rtc_rw_fail: + spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); + return rc; +} + +static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + int rc; + u8 value[NUM_8_BIT_RTC_REGS]; + unsigned long secs; + struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + + rc = pm8xxx_read_wrapper(rtc_dd, value, rtc_dd->alarm_rw_base, + NUM_8_BIT_RTC_REGS); + if (rc < 0) { + dev_err(dev, "RTC alarm time read failed\n"); + return rc; + } + + secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); + + rtc_time_to_tm(secs, &alarm->time); + + rc = rtc_valid_tm(&alarm->time); + if (rc < 0) { + dev_err(dev, "Invalid alarm time read from RTC\n"); + return rc; + } + + dev_dbg(dev, "Alarm set for - h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n", + alarm->time.tm_hour, alarm->time.tm_min, + alarm->time.tm_sec, alarm->time.tm_mday, + alarm->time.tm_mon, alarm->time.tm_year); + + return 0; +} + +static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + int rc; + unsigned long irq_flags; + struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + u8 ctrl_reg; + + spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); + ctrl_reg = rtc_dd->ctrl_reg; + ctrl_reg = (enable) ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) : + (ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE); + + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); + if (rc < 0) { + dev_err(dev, "Write to RTC control register failed\n"); + goto rtc_rw_fail; + } + + rtc_dd->ctrl_reg = ctrl_reg; + +rtc_rw_fail: + spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); + return rc; +} + +static struct rtc_class_ops pm8xxx_rtc_ops = { + .read_time = pm8xxx_rtc_read_time, + .set_alarm = pm8xxx_rtc_set_alarm, + .read_alarm = pm8xxx_rtc_read_alarm, + .alarm_irq_enable = pm8xxx_rtc_alarm_irq_enable, +}; + +static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id) +{ + struct pm8xxx_rtc *rtc_dd = dev_id; + u8 ctrl_reg; + int rc; + unsigned long irq_flags; + + rtc_update_irq(rtc_dd->rtc, 1, RTC_IRQF | RTC_AF); + + spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); + + /* Clear the alarm enable bit */ + ctrl_reg = rtc_dd->ctrl_reg; + ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE; + + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); + if (rc < 0) { + spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); + dev_err(rtc_dd->rtc_dev, "Write to RTC control register " + "failed\n"); + goto rtc_alarm_handled; + } + + rtc_dd->ctrl_reg = ctrl_reg; + spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); + + /* Clear RTC alarm register */ + rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base + + PM8XXX_ALARM_CTRL_OFFSET, 1); + if (rc < 0) { + dev_err(rtc_dd->rtc_dev, "RTC Alarm control register read " + "failed\n"); + goto rtc_alarm_handled; + } + + ctrl_reg &= ~PM8xxx_RTC_ALARM_CLEAR; + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base + + PM8XXX_ALARM_CTRL_OFFSET, 1); + if (rc < 0) + dev_err(rtc_dd->rtc_dev, "Write to RTC Alarm control register" + " failed\n"); + +rtc_alarm_handled: + return IRQ_HANDLED; +} + +static int __devinit pm8xxx_rtc_probe(struct platform_device *pdev) +{ + int rc; + u8 ctrl_reg; + bool rtc_write_enable = false; + struct pm8xxx_rtc *rtc_dd; + struct resource *rtc_resource; + const struct pm8xxx_rtc_platform_data *pdata = + dev_get_platdata(&pdev->dev); + + if (pdata != NULL) + rtc_write_enable = pdata->rtc_write_enable; + + rtc_dd = kzalloc(sizeof(*rtc_dd), GFP_KERNEL); + if (rtc_dd == NULL) { + dev_err(&pdev->dev, "Unable to allocate memory!\n"); + return -ENOMEM; + } + + /* Initialise spinlock to protect RTC control register */ + spin_lock_init(&rtc_dd->ctrl_reg_lock); + + rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0); + if (rtc_dd->rtc_alarm_irq < 0) { + dev_err(&pdev->dev, "Alarm IRQ resource absent!\n"); + rc = -ENXIO; + goto fail_rtc_enable; + } + + rtc_resource = platform_get_resource_byname(pdev, IORESOURCE_IO, + "pmic_rtc_base"); + if (!(rtc_resource && rtc_resource->start)) { + dev_err(&pdev->dev, "RTC IO resource absent!\n"); + rc = -ENXIO; + goto fail_rtc_enable; + } + + rtc_dd->rtc_base = rtc_resource->start; + + /* Setup RTC register addresses */ + rtc_dd->rtc_write_base = rtc_dd->rtc_base + PM8XXX_RTC_WRITE_OFFSET; + rtc_dd->rtc_read_base = rtc_dd->rtc_base + PM8XXX_RTC_READ_OFFSET; + rtc_dd->alarm_rw_base = rtc_dd->rtc_base + PM8XXX_ALARM_RW_OFFSET; + + rtc_dd->rtc_dev = &pdev->dev; + + /* Check if the RTC is on, else turn it on */ + rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); + if (rc < 0) { + dev_err(&pdev->dev, "RTC control register read failed!\n"); + goto fail_rtc_enable; + } + + if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) { + ctrl_reg |= PM8xxx_RTC_ENABLE; + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, + 1); + if (rc < 0) { + dev_err(&pdev->dev, "Write to RTC control register " + "failed\n"); + goto fail_rtc_enable; + } + } + + rtc_dd->ctrl_reg = ctrl_reg; + if (rtc_write_enable == true) + pm8xxx_rtc_ops.set_time = pm8xxx_rtc_set_time; + + platform_set_drvdata(pdev, rtc_dd); + + /* Register the RTC device */ + rtc_dd->rtc = rtc_device_register("pm8xxx_rtc", &pdev->dev, + &pm8xxx_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc_dd->rtc)) { + dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n", + __func__, PTR_ERR(rtc_dd->rtc)); + rc = PTR_ERR(rtc_dd->rtc); + goto fail_rtc_enable; + } + + /* Request the alarm IRQ */ + rc = request_any_context_irq(rtc_dd->rtc_alarm_irq, + pm8xxx_alarm_trigger, IRQF_TRIGGER_RISING, + "pm8xxx_rtc_alarm", rtc_dd); + if (rc < 0) { + dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc); + goto fail_req_irq; + } + + device_init_wakeup(&pdev->dev, 1); + + dev_dbg(&pdev->dev, "Probe success !!\n"); + + return 0; + +fail_req_irq: + rtc_device_unregister(rtc_dd->rtc); +fail_rtc_enable: + platform_set_drvdata(pdev, NULL); + kfree(rtc_dd); + return rc; +} + +static int __devexit pm8xxx_rtc_remove(struct platform_device *pdev) +{ + struct pm8xxx_rtc *rtc_dd = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + free_irq(rtc_dd->rtc_alarm_irq, rtc_dd); + rtc_device_unregister(rtc_dd->rtc); + platform_set_drvdata(pdev, NULL); + kfree(rtc_dd); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int pm8xxx_rtc_resume(struct device *dev) +{ + struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rtc_dd->rtc_alarm_irq); + + return 0; +} + +static int pm8xxx_rtc_suspend(struct device *dev) +{ + struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rtc_dd->rtc_alarm_irq); + + return 0; +} +#endif + +SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, pm8xxx_rtc_suspend, pm8xxx_rtc_resume); + +static struct platform_driver pm8xxx_rtc_driver = { + .probe = pm8xxx_rtc_probe, + .remove = __devexit_p(pm8xxx_rtc_remove), + .driver = { + .name = PM8XXX_RTC_DEV_NAME, + .owner = THIS_MODULE, + .pm = &pm8xxx_rtc_pm_ops, + }, +}; + +static int __init pm8xxx_rtc_init(void) +{ + return platform_driver_register(&pm8xxx_rtc_driver); +} +module_init(pm8xxx_rtc_init); + +static void __exit pm8xxx_rtc_exit(void) +{ + platform_driver_unregister(&pm8xxx_rtc_driver); +} +module_exit(pm8xxx_rtc_exit); + +MODULE_ALIAS("platform:rtc-pm8xxx"); +MODULE_DESCRIPTION("PMIC8xxx RTC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Anirudh Ghayal "); diff --git a/include/linux/mfd/pm8xxx/rtc.h b/include/linux/mfd/pm8xxx/rtc.h new file mode 100644 index 000000000000..14f1983eaecc --- /dev/null +++ b/include/linux/mfd/pm8xxx/rtc.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __RTC_PM8XXX_H__ +#define __RTC_PM8XXX_H__ + +#define PM8XXX_RTC_DEV_NAME "rtc-pm8xxx" +/** + * struct pm8xxx_rtc_pdata - RTC driver platform data + * @rtc_write_enable: variable stating RTC write capability + */ +struct pm8xxx_rtc_platform_data { + bool rtc_write_enable; +}; + +#endif /* __RTC_PM8XXX_H__ */ -- cgit v1.2.3 From 8f6b0dd369868559b384a66aa17512ae5aae2d9b Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 25 Jul 2011 17:13:34 -0700 Subject: drivers/rtc/rtc-twl.c: check return value of twl_rtc_write_u8() in twl_rtc_set_time() We forget to save the return value of the call to twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG); in 'ret', making the test of 'ret < 0' dead code since 'ret' then couldn't possibly have changed since the last test just a few lines above. It also makes us not detect failures from that specific twl_rtc_write_u8() call. Signed-off-by: Jesper Juhl Cc: Alessandro Zummo Cc: Alexandre Rusev Cc: "George G. Davis" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-twl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index f9a2799c44d6..9a81f778d6b2 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -275,7 +275,7 @@ static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm) goto out; save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M; - twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG); + ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG); if (ret < 0) goto out; -- cgit v1.2.3 From e57ee01750c4954fd0b5e3c6109cd4b870880eb9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 25 Jul 2011 17:13:34 -0700 Subject: drivers/rtc/rtc-tegra.c: properly initialize spinlock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using __SPIN_LOCK_UNLOCKED for a dynamically allocated lock is wrong and breaks the build with PREEMPT_RT_FULL. Signed-off-by: Uwe Kleine-König Cc: Andrew Chew Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c index 2fc31aac3f4e..75259fe38602 100644 --- a/drivers/rtc/rtc-tegra.c +++ b/drivers/rtc/rtc-tegra.c @@ -343,7 +343,7 @@ static int __devinit tegra_rtc_probe(struct platform_device *pdev) /* set context info. */ info->pdev = pdev; - info->tegra_rtc_lock = __SPIN_LOCK_UNLOCKED(info->tegra_rtc_lock); + spin_lock_init(&info->tegra_rtc_lock); platform_set_drvdata(pdev, info); -- cgit v1.2.3 From 2c4f57d12df7696d65b0247bfd57fd082a7719e6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 26 Jul 2011 16:08:18 -0700 Subject: rtc: handle errors correctly in rtc_irq_set_state() The code checks the correctness of the parameters, but unconditionally arms/disarms the hrtimer. The result is that a random task might arm/disarm rtc timer and surprise the real owner by either generating events or by stopping them. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Ingo Molnar Cc: Ben Greear Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/interface.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index df68618f6dbb..b6bf57f25cc9 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -656,6 +656,8 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled err = -EBUSY; if (rtc->irq_task != task) err = -EACCES; + if (err) + goto out; if (enabled) { ktime_t period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); @@ -664,6 +666,7 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled hrtimer_cancel(&rtc->pie_timer); } rtc->pie_enabled = enabled; +out: spin_unlock_irqrestore(&rtc->irq_task_lock, flags); return err; -- cgit v1.2.3 From 431e2bcc371016824f419baa745f82388258f3ee Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 26 Jul 2011 16:08:19 -0700 Subject: rtc: limit frequency Due to the hrtimer self rearming mode a user can DoS the machine simply because it's starved by hrtimer events. The RTC hrtimer is self rearming. We really need to limit the frequency to something sensible. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Ingo Molnar Cc: Ben Greear Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index b6bf57f25cc9..601b053bc0e5 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -688,7 +688,7 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq) int err = 0; unsigned long flags; - if (freq <= 0) + if (freq <= 0 || freq > 5000) return -EINVAL; spin_lock_irqsave(&rtc->irq_task_lock, flags); -- cgit v1.2.3 From b830ac1d9a2262093bb0f3f6a2fd2a1c8278daf5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 26 Jul 2011 16:08:20 -0700 Subject: rtc: fix hrtimer deadlock Ben reported a lockup related to rtc. The lockup happens due to: CPU0 CPU1 rtc_irq_set_state() __run_hrtimer() spin_lock_irqsave(&rtc->irq_task_lock) rtc_handle_legacy_irq(); spin_lock(&rtc->irq_task_lock); hrtimer_cancel() while (callback_running); So the running callback never finishes as it's blocked on rtc->irq_task_lock. Use hrtimer_try_to_cancel() instead and drop rtc->irq_task_lock while waiting for the callback. Fix this for both rtc_irq_set_state() and rtc_irq_set_freq(). Signed-off-by: Thomas Gleixner Reported-by: Ben Greear Cc: John Stultz Cc: Ingo Molnar Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/interface.c | 56 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 19 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 601b053bc0e5..3195dbd3ec34 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -636,6 +636,29 @@ void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task) } EXPORT_SYMBOL_GPL(rtc_irq_unregister); +static int rtc_update_hrtimer(struct rtc_device *rtc, int enabled) +{ + /* + * We unconditionally cancel the timer here, because otherwise + * we could run into BUG_ON(timer->state != HRTIMER_STATE_CALLBACK); + * when we manage to start the timer before the callback + * returns HRTIMER_RESTART. + * + * We cannot use hrtimer_cancel() here as a running callback + * could be blocked on rtc->irq_task_lock and hrtimer_cancel() + * would spin forever. + */ + if (hrtimer_try_to_cancel(&rtc->pie_timer) < 0) + return -1; + + if (enabled) { + ktime_t period = ktime_set(0, NSEC_PER_SEC / rtc->irq_freq); + + hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL); + } + return 0; +} + /** * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs * @rtc: the rtc device @@ -651,24 +674,21 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled int err = 0; unsigned long flags; +retry: spin_lock_irqsave(&rtc->irq_task_lock, flags); if (rtc->irq_task != NULL && task == NULL) err = -EBUSY; if (rtc->irq_task != task) err = -EACCES; - if (err) - goto out; - - if (enabled) { - ktime_t period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); - hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL); - } else { - hrtimer_cancel(&rtc->pie_timer); + if (!err) { + if (rtc_update_hrtimer(rtc, enabled) < 0) { + spin_unlock_irqrestore(&rtc->irq_task_lock, flags); + cpu_relax(); + goto retry; + } + rtc->pie_enabled = enabled; } - rtc->pie_enabled = enabled; -out: spin_unlock_irqrestore(&rtc->irq_task_lock, flags); - return err; } EXPORT_SYMBOL_GPL(rtc_irq_set_state); @@ -690,20 +710,18 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq) if (freq <= 0 || freq > 5000) return -EINVAL; - +retry: spin_lock_irqsave(&rtc->irq_task_lock, flags); if (rtc->irq_task != NULL && task == NULL) err = -EBUSY; if (rtc->irq_task != task) err = -EACCES; - if (err == 0) { + if (!err) { rtc->irq_freq = freq; - if (rtc->pie_enabled) { - ktime_t period; - hrtimer_cancel(&rtc->pie_timer); - period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); - hrtimer_start(&rtc->pie_timer, period, - HRTIMER_MODE_REL); + if (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) { + spin_unlock_irqrestore(&rtc->irq_task_lock, flags); + cpu_relax(); + goto retry; } } spin_unlock_irqrestore(&rtc->irq_task_lock, flags); -- cgit v1.2.3