forked from Minki/linux
rtc: sun6i: Add some locking
Some registers have a read-modify-write access pattern that are not atomic. Add some locking to prevent from concurrent accesses. Cc: stable@vger.kernel.org Acked-by: Chen-Yu Tsai <wens@csie.org> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
This commit is contained in:
parent
3753941475
commit
a9422a19ce
@ -114,13 +114,17 @@ struct sun6i_rtc_dev {
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
unsigned long alarm;
|
||||
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
|
||||
{
|
||||
struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 val;
|
||||
|
||||
spin_lock(&chip->lock);
|
||||
val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
|
||||
|
||||
if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
|
||||
@ -129,10 +133,11 @@ static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
|
||||
|
||||
rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
spin_unlock(&chip->lock);
|
||||
|
||||
return IRQ_NONE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
|
||||
@ -140,6 +145,7 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
|
||||
u32 alrm_val = 0;
|
||||
u32 alrm_irq_val = 0;
|
||||
u32 alrm_wake_val = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (to) {
|
||||
alrm_val = SUN6I_ALRM_EN_CNT_EN;
|
||||
@ -150,9 +156,11 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
|
||||
chip->base + SUN6I_ALRM_IRQ_STA);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&chip->lock, flags);
|
||||
writel(alrm_val, chip->base + SUN6I_ALRM_EN);
|
||||
writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
|
||||
writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG);
|
||||
spin_unlock_irqrestore(&chip->lock, flags);
|
||||
}
|
||||
|
||||
static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
|
||||
@ -191,11 +199,15 @@ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
|
||||
static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
|
||||
{
|
||||
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
u32 alrm_st;
|
||||
u32 alrm_en;
|
||||
|
||||
spin_lock_irqsave(&chip->lock, flags);
|
||||
alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
|
||||
alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA);
|
||||
spin_unlock_irqrestore(&chip->lock, flags);
|
||||
|
||||
wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
|
||||
wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
|
||||
rtc_time_to_tm(chip->alarm, &wkalrm->time);
|
||||
@ -356,6 +368,7 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
|
||||
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
spin_lock_init(&chip->lock);
|
||||
|
||||
platform_set_drvdata(pdev, chip);
|
||||
chip->dev = &pdev->dev;
|
||||
|
Loading…
Reference in New Issue
Block a user