diff options
author | Maxime Ripard <maxime.ripard@free-electrons.com> | 2017-01-23 05:41:47 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-03-12 00:41:52 -0500 |
commit | 6aae7ffa33072e6d3a208039ac015c1a0fb51e37 (patch) | |
tree | 1eb14912f08a6129cb086054085967d873eaaf77 | |
parent | 616f5ef61338a53e997eebbe2c13b21c40df9b86 (diff) |
rtc: sun6i: Add some locking
commit a9422a19ce270a22fc520f2278fb7e80c58be508 upstream.
Some registers have a read-modify-write access pattern that are not atomic.
Add some locking to prevent from concurrent accesses.
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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-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; |