mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
Merge branch 'irq-pm'
* irq-pm: genirq / PM: describe IRQF_COND_SUSPEND tty: serial: atmel: rework interrupt and wakeup handling watchdog: at91sam9: request the irq with IRQF_NO_SUSPEND clk: at91: implement suspend/resume for the PMC irqchip rtc: at91rm9200: rework wakeup and interrupt handling rtc: at91sam9: rework wakeup and interrupt handling PM / wakeup: export pm_system_wakeup symbol genirq / PM: Add flag for shared NO_SUSPEND interrupt lines genirq / PM: better describe IRQF_NO_SUSPEND semantics
This commit is contained in:
commit
79d223646b
12 changed files with 215 additions and 40 deletions
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "rtc-at91rm9200.h"
|
||||
|
|
@ -54,6 +55,10 @@ static void __iomem *at91_rtc_regs;
|
|||
static int irq;
|
||||
static DEFINE_SPINLOCK(at91_rtc_lock);
|
||||
static u32 at91_rtc_shadow_imr;
|
||||
static bool suspended;
|
||||
static DEFINE_SPINLOCK(suspended_lock);
|
||||
static unsigned long cached_events;
|
||||
static u32 at91_rtc_imr;
|
||||
|
||||
static void at91_rtc_write_ier(u32 mask)
|
||||
{
|
||||
|
|
@ -290,7 +295,9 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id)
|
|||
struct rtc_device *rtc = platform_get_drvdata(pdev);
|
||||
unsigned int rtsr;
|
||||
unsigned long events = 0;
|
||||
int ret = IRQ_NONE;
|
||||
|
||||
spin_lock(&suspended_lock);
|
||||
rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read_imr();
|
||||
if (rtsr) { /* this interrupt is shared! Is it ours? */
|
||||
if (rtsr & AT91_RTC_ALARM)
|
||||
|
|
@ -304,14 +311,22 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id)
|
|||
|
||||
at91_rtc_write(AT91_RTC_SCCR, rtsr); /* clear status reg */
|
||||
|
||||
rtc_update_irq(rtc, 1, events);
|
||||
if (!suspended) {
|
||||
rtc_update_irq(rtc, 1, events);
|
||||
|
||||
dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n", __func__,
|
||||
events >> 8, events & 0x000000FF);
|
||||
dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n",
|
||||
__func__, events >> 8, events & 0x000000FF);
|
||||
} else {
|
||||
cached_events |= events;
|
||||
at91_rtc_write_idr(at91_rtc_imr);
|
||||
pm_system_wakeup();
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
return IRQ_NONE; /* not handled */
|
||||
spin_lock(&suspended_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct at91_rtc_config at91rm9200_config = {
|
||||
|
|
@ -401,8 +416,8 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
|
|||
AT91_RTC_CALEV);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, at91_rtc_interrupt,
|
||||
IRQF_SHARED,
|
||||
"at91_rtc", pdev);
|
||||
IRQF_SHARED | IRQF_COND_SUSPEND,
|
||||
"at91_rtc", pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "IRQ %d already in use.\n", irq);
|
||||
return ret;
|
||||
|
|
@ -454,8 +469,6 @@ static void at91_rtc_shutdown(struct platform_device *pdev)
|
|||
|
||||
/* AT91RM9200 RTC Power management control */
|
||||
|
||||
static u32 at91_rtc_imr;
|
||||
|
||||
static int at91_rtc_suspend(struct device *dev)
|
||||
{
|
||||
/* this IRQ is shared with DBGU and other hardware which isn't
|
||||
|
|
@ -464,21 +477,42 @@ static int at91_rtc_suspend(struct device *dev)
|
|||
at91_rtc_imr = at91_rtc_read_imr()
|
||||
& (AT91_RTC_ALARM|AT91_RTC_SECEV);
|
||||
if (at91_rtc_imr) {
|
||||
if (device_may_wakeup(dev))
|
||||
if (device_may_wakeup(dev)) {
|
||||
unsigned long flags;
|
||||
|
||||
enable_irq_wake(irq);
|
||||
else
|
||||
|
||||
spin_lock_irqsave(&suspended_lock, flags);
|
||||
suspended = true;
|
||||
spin_unlock_irqrestore(&suspended_lock, flags);
|
||||
} else {
|
||||
at91_rtc_write_idr(at91_rtc_imr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct rtc_device *rtc = dev_get_drvdata(dev);
|
||||
|
||||
if (at91_rtc_imr) {
|
||||
if (device_may_wakeup(dev))
|
||||
if (device_may_wakeup(dev)) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&suspended_lock, flags);
|
||||
|
||||
if (cached_events) {
|
||||
rtc_update_irq(rtc, 1, cached_events);
|
||||
cached_events = 0;
|
||||
}
|
||||
|
||||
suspended = false;
|
||||
spin_unlock_irqrestore(&suspended_lock, flags);
|
||||
|
||||
disable_irq_wake(irq);
|
||||
else
|
||||
at91_rtc_write_ier(at91_rtc_imr);
|
||||
}
|
||||
at91_rtc_write_ier(at91_rtc_imr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
/*
|
||||
|
|
@ -77,6 +78,9 @@ struct sam9_rtc {
|
|||
unsigned int gpbr_offset;
|
||||
int irq;
|
||||
struct clk *sclk;
|
||||
bool suspended;
|
||||
unsigned long events;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
#define rtt_readl(rtc, field) \
|
||||
|
|
@ -271,14 +275,9 @@ static int at91_rtc_proc(struct device *dev, struct seq_file *seq)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ handler for the RTC
|
||||
*/
|
||||
static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc)
|
||||
static irqreturn_t at91_rtc_cache_events(struct sam9_rtc *rtc)
|
||||
{
|
||||
struct sam9_rtc *rtc = _rtc;
|
||||
u32 sr, mr;
|
||||
unsigned long events = 0;
|
||||
|
||||
/* Shared interrupt may be for another device. Note: reading
|
||||
* SR clears it, so we must only read it in this irq handler!
|
||||
|
|
@ -290,20 +289,56 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc)
|
|||
|
||||
/* alarm status */
|
||||
if (sr & AT91_RTT_ALMS)
|
||||
events |= (RTC_AF | RTC_IRQF);
|
||||
rtc->events |= (RTC_AF | RTC_IRQF);
|
||||
|
||||
/* timer update/increment */
|
||||
if (sr & AT91_RTT_RTTINC)
|
||||
events |= (RTC_UF | RTC_IRQF);
|
||||
|
||||
rtc_update_irq(rtc->rtcdev, 1, events);
|
||||
|
||||
pr_debug("%s: num=%ld, events=0x%02lx\n", __func__,
|
||||
events >> 8, events & 0x000000FF);
|
||||
rtc->events |= (RTC_UF | RTC_IRQF);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void at91_rtc_flush_events(struct sam9_rtc *rtc)
|
||||
{
|
||||
if (!rtc->events)
|
||||
return;
|
||||
|
||||
rtc_update_irq(rtc->rtcdev, 1, rtc->events);
|
||||
rtc->events = 0;
|
||||
|
||||
pr_debug("%s: num=%ld, events=0x%02lx\n", __func__,
|
||||
rtc->events >> 8, rtc->events & 0x000000FF);
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ handler for the RTC
|
||||
*/
|
||||
static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc)
|
||||
{
|
||||
struct sam9_rtc *rtc = _rtc;
|
||||
int ret;
|
||||
|
||||
spin_lock(&rtc->lock);
|
||||
|
||||
ret = at91_rtc_cache_events(rtc);
|
||||
|
||||
/* We're called in suspended state */
|
||||
if (rtc->suspended) {
|
||||
/* Mask irqs coming from this peripheral */
|
||||
rtt_writel(rtc, MR,
|
||||
rtt_readl(rtc, MR) &
|
||||
~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
|
||||
/* Trigger a system wakeup */
|
||||
pm_system_wakeup();
|
||||
} else {
|
||||
at91_rtc_flush_events(rtc);
|
||||
}
|
||||
|
||||
spin_unlock(&rtc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops at91_rtc_ops = {
|
||||
.read_time = at91_rtc_readtime,
|
||||
.set_time = at91_rtc_settime,
|
||||
|
|
@ -421,7 +456,8 @@ static int at91_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
/* register irq handler after we know what name we'll use */
|
||||
ret = devm_request_irq(&pdev->dev, rtc->irq, at91_rtc_interrupt,
|
||||
IRQF_SHARED, dev_name(&rtc->rtcdev->dev), rtc);
|
||||
IRQF_SHARED | IRQF_COND_SUSPEND,
|
||||
dev_name(&rtc->rtcdev->dev), rtc);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "can't share IRQ %d?\n", rtc->irq);
|
||||
return ret;
|
||||
|
|
@ -482,7 +518,12 @@ static int at91_rtc_suspend(struct device *dev)
|
|||
rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
|
||||
if (rtc->imr) {
|
||||
if (device_may_wakeup(dev) && (mr & AT91_RTT_ALMIEN)) {
|
||||
unsigned long flags;
|
||||
|
||||
enable_irq_wake(rtc->irq);
|
||||
spin_lock_irqsave(&rtc->lock, flags);
|
||||
rtc->suspended = true;
|
||||
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||
/* don't let RTTINC cause wakeups */
|
||||
if (mr & AT91_RTT_RTTINCIEN)
|
||||
rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN);
|
||||
|
|
@ -499,10 +540,18 @@ static int at91_rtc_resume(struct device *dev)
|
|||
u32 mr;
|
||||
|
||||
if (rtc->imr) {
|
||||
unsigned long flags;
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(rtc->irq);
|
||||
mr = rtt_readl(rtc, MR);
|
||||
rtt_writel(rtc, MR, mr | rtc->imr);
|
||||
|
||||
spin_lock_irqsave(&rtc->lock, flags);
|
||||
rtc->suspended = false;
|
||||
at91_rtc_cache_events(rtc);
|
||||
at91_rtc_flush_events(rtc);
|
||||
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue