diff options
| author | Maxime Ripard <maxime.ripard@free-electrons.com> | 2017-01-23 05:41:47 -0500 |
|---|---|---|
| committer | Alexandre Belloni <alexandre.belloni@free-electrons.com> | 2017-02-01 06:42:39 -0500 |
| commit | a9422a19ce270a22fc520f2278fb7e80c58be508 (patch) | |
| tree | 6507b1553c16b5b20a87eed7b7015e3b07cfa910 /drivers/rtc | |
| parent | 3753941475ae6501dcd1e41832bd0e6c35247d6a (diff) | |
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>
Diffstat (limited to 'drivers/rtc')
| -rw-r--r-- | drivers/rtc/rtc-sun6i.c | 17 |
1 files changed, 15 insertions, 2 deletions
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index a6136cb99851..72086f0c0782 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c | |||
| @@ -114,13 +114,17 @@ struct sun6i_rtc_dev { | |||
| 114 | void __iomem *base; | 114 | void __iomem *base; |
| 115 | int irq; | 115 | int irq; |
| 116 | unsigned long alarm; | 116 | unsigned long alarm; |
| 117 | |||
| 118 | spinlock_t lock; | ||
| 117 | }; | 119 | }; |
| 118 | 120 | ||
| 119 | static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) | 121 | static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) |
| 120 | { | 122 | { |
| 121 | struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; | 123 | struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; |
| 124 | irqreturn_t ret = IRQ_NONE; | ||
| 122 | u32 val; | 125 | u32 val; |
| 123 | 126 | ||
| 127 | spin_lock(&chip->lock); | ||
| 124 | val = readl(chip->base + SUN6I_ALRM_IRQ_STA); | 128 | val = readl(chip->base + SUN6I_ALRM_IRQ_STA); |
| 125 | 129 | ||
| 126 | if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { | 130 | if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { |
| @@ -129,10 +133,11 @@ static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) | |||
| 129 | 133 | ||
| 130 | rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); | 134 | rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); |
| 131 | 135 | ||
| 132 | return IRQ_HANDLED; | 136 | ret = IRQ_HANDLED; |
| 133 | } | 137 | } |
| 138 | spin_unlock(&chip->lock); | ||
| 134 | 139 | ||
| 135 | return IRQ_NONE; | 140 | return ret; |
| 136 | } | 141 | } |
| 137 | 142 | ||
| 138 | static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) | 143 | 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 | u32 alrm_val = 0; | 145 | u32 alrm_val = 0; |
| 141 | u32 alrm_irq_val = 0; | 146 | u32 alrm_irq_val = 0; |
| 142 | u32 alrm_wake_val = 0; | 147 | u32 alrm_wake_val = 0; |
| 148 | unsigned long flags; | ||
| 143 | 149 | ||
| 144 | if (to) { | 150 | if (to) { |
| 145 | alrm_val = SUN6I_ALRM_EN_CNT_EN; | 151 | alrm_val = SUN6I_ALRM_EN_CNT_EN; |
| @@ -150,9 +156,11 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) | |||
| 150 | chip->base + SUN6I_ALRM_IRQ_STA); | 156 | chip->base + SUN6I_ALRM_IRQ_STA); |
| 151 | } | 157 | } |
| 152 | 158 | ||
| 159 | spin_lock_irqsave(&chip->lock, flags); | ||
| 153 | writel(alrm_val, chip->base + SUN6I_ALRM_EN); | 160 | writel(alrm_val, chip->base + SUN6I_ALRM_EN); |
| 154 | writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); | 161 | writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); |
| 155 | writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); | 162 | writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); |
| 163 | spin_unlock_irqrestore(&chip->lock, flags); | ||
| 156 | } | 164 | } |
| 157 | 165 | ||
| 158 | static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) | 166 | 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 | static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) | 199 | static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) |
| 192 | { | 200 | { |
| 193 | struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); | 201 | struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); |
| 202 | unsigned long flags; | ||
| 194 | u32 alrm_st; | 203 | u32 alrm_st; |
| 195 | u32 alrm_en; | 204 | u32 alrm_en; |
| 196 | 205 | ||
| 206 | spin_lock_irqsave(&chip->lock, flags); | ||
| 197 | alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); | 207 | alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); |
| 198 | alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); | 208 | alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); |
| 209 | spin_unlock_irqrestore(&chip->lock, flags); | ||
| 210 | |||
| 199 | wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN); | 211 | wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN); |
| 200 | wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); | 212 | wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); |
| 201 | rtc_time_to_tm(chip->alarm, &wkalrm->time); | 213 | rtc_time_to_tm(chip->alarm, &wkalrm->time); |
| @@ -356,6 +368,7 @@ static int sun6i_rtc_probe(struct platform_device *pdev) | |||
| 356 | chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); | 368 | chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); |
| 357 | if (!chip) | 369 | if (!chip) |
| 358 | return -ENOMEM; | 370 | return -ENOMEM; |
| 371 | spin_lock_init(&chip->lock); | ||
| 359 | 372 | ||
| 360 | platform_set_drvdata(pdev, chip); | 373 | platform_set_drvdata(pdev, chip); |
| 361 | chip->dev = &pdev->dev; | 374 | chip->dev = &pdev->dev; |
