Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os 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>

authored by

Maxime Ripard and committed by
Alexandre Belloni
a9422a19 37539414

+15 -2
+15 -2
drivers/rtc/rtc-sun6i.c
··· 114 114 void __iomem *base; 115 115 int irq; 116 116 unsigned long alarm; 117 + 118 + spinlock_t lock; 117 119 }; 118 120 119 121 static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) 120 122 { 121 123 struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; 124 + irqreturn_t ret = IRQ_NONE; 122 125 u32 val; 123 126 127 + spin_lock(&chip->lock); 124 128 val = readl(chip->base + SUN6I_ALRM_IRQ_STA); 125 129 126 130 if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { ··· 133 129 134 130 rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); 135 131 136 - return IRQ_HANDLED; 132 + ret = IRQ_HANDLED; 137 133 } 134 + spin_unlock(&chip->lock); 138 135 139 - return IRQ_NONE; 136 + return ret; 140 137 } 141 138 142 139 static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) ··· 145 140 u32 alrm_val = 0; 146 141 u32 alrm_irq_val = 0; 147 142 u32 alrm_wake_val = 0; 143 + unsigned long flags; 148 144 149 145 if (to) { 150 146 alrm_val = SUN6I_ALRM_EN_CNT_EN; ··· 156 150 chip->base + SUN6I_ALRM_IRQ_STA); 157 151 } 158 152 153 + spin_lock_irqsave(&chip->lock, flags); 159 154 writel(alrm_val, chip->base + SUN6I_ALRM_EN); 160 155 writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); 161 156 writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); 157 + spin_unlock_irqrestore(&chip->lock, flags); 162 158 } 163 159 164 160 static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) ··· 199 191 static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) 200 192 { 201 193 struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); 194 + unsigned long flags; 202 195 u32 alrm_st; 203 196 u32 alrm_en; 204 197 198 + spin_lock_irqsave(&chip->lock, flags); 205 199 alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); 206 200 alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); 201 + spin_unlock_irqrestore(&chip->lock, flags); 202 + 207 203 wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN); 208 204 wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); 209 205 rtc_time_to_tm(chip->alarm, &wkalrm->time); ··· 368 356 chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); 369 357 if (!chip) 370 358 return -ENOMEM; 359 + spin_lock_init(&chip->lock); 371 360 372 361 platform_set_drvdata(pdev, chip); 373 362 chip->dev = &pdev->dev;