Commit a9422a19 authored by Maxime Ripard's avatar Maxime Ripard Committed by Alexandre Belloni

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: default avatarChen-Yu Tsai <wens@csie.org>
Signed-off-by: default avatarMaxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@free-electrons.com>
parent 37539414
...@@ -114,13 +114,17 @@ struct sun6i_rtc_dev { ...@@ -114,13 +114,17 @@ struct sun6i_rtc_dev {
void __iomem *base; void __iomem *base;
int irq; int irq;
unsigned long alarm; unsigned long alarm;
spinlock_t lock;
}; };
static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
{ {
struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
irqreturn_t ret = IRQ_NONE;
u32 val; u32 val;
spin_lock(&chip->lock);
val = readl(chip->base + SUN6I_ALRM_IRQ_STA); val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
...@@ -129,10 +133,11 @@ static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) ...@@ -129,10 +133,11 @@ static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); 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) 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) ...@@ -140,6 +145,7 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
u32 alrm_val = 0; u32 alrm_val = 0;
u32 alrm_irq_val = 0; u32 alrm_irq_val = 0;
u32 alrm_wake_val = 0; u32 alrm_wake_val = 0;
unsigned long flags;
if (to) { if (to) {
alrm_val = SUN6I_ALRM_EN_CNT_EN; alrm_val = SUN6I_ALRM_EN_CNT_EN;
...@@ -150,9 +156,11 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) ...@@ -150,9 +156,11 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
chip->base + SUN6I_ALRM_IRQ_STA); chip->base + SUN6I_ALRM_IRQ_STA);
} }
spin_lock_irqsave(&chip->lock, flags);
writel(alrm_val, chip->base + SUN6I_ALRM_EN); writel(alrm_val, chip->base + SUN6I_ALRM_EN);
writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); 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) 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) ...@@ -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) static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{ {
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
unsigned long flags;
u32 alrm_st; u32 alrm_st;
u32 alrm_en; u32 alrm_en;
spin_lock_irqsave(&chip->lock, flags);
alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); 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->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
rtc_time_to_tm(chip->alarm, &wkalrm->time); rtc_time_to_tm(chip->alarm, &wkalrm->time);
...@@ -356,6 +368,7 @@ static int sun6i_rtc_probe(struct platform_device *pdev) ...@@ -356,6 +368,7 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip) if (!chip)
return -ENOMEM; return -ENOMEM;
spin_lock_init(&chip->lock);
platform_set_drvdata(pdev, chip); platform_set_drvdata(pdev, chip);
chip->dev = &pdev->dev; chip->dev = &pdev->dev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment