]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blame - releases/4.19.51/pwm-meson-use-the-spin-lock-only-to-protect-register.patch
Linux 4.19.51
[thirdparty/kernel/stable-queue.git] / releases / 4.19.51 / pwm-meson-use-the-spin-lock-only-to-protect-register.patch
CommitLineData
37554d48
SL
1From aac4c49cf718182439225690a841d6a706aa8c1b Mon Sep 17 00:00:00 2001
2From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
3Date: Mon, 1 Apr 2019 19:57:48 +0200
4Subject: pwm: meson: Use the spin-lock only to protect register modifications
5MIME-Version: 1.0
6Content-Type: text/plain; charset=UTF-8
7Content-Transfer-Encoding: 8bit
8
9[ Upstream commit f173747fffdf037c791405ab4f1ec0eb392fc48e ]
10
11Holding the spin-lock for all of the code in meson_pwm_apply() can
12result in a "BUG: scheduling while atomic". This can happen because
13clk_get_rate() (which is called from meson_pwm_calc()) may sleep.
14Only hold the spin-lock when modifying registers to solve this.
15
16The reason why we need a spin-lock in the driver is because the
17REG_MISC_AB register is shared between the two channels provided by one
18PWM controller. The only functions where REG_MISC_AB is modified are
19meson_pwm_enable() and meson_pwm_disable() so the register reads/writes
20in there need to be protected by the spin-lock.
21
22The original code also used the spin-lock to protect the values in
23struct meson_pwm_channel. This could be necessary if two consumers can
24use the same PWM channel. However, PWM core doesn't allow this so we
25don't need to protect the values in struct meson_pwm_channel with a
26lock.
27
28Fixes: 211ed630753d2f ("pwm: Add support for Meson PWM Controller")
29Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
30Reviewed-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
31Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
32Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
33Signed-off-by: Sasha Levin <sashal@kernel.org>
34---
35 drivers/pwm/pwm-meson.c | 25 +++++++++++++++++--------
36 1 file changed, 17 insertions(+), 8 deletions(-)
37
38diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c
39index c1ed641b3e26..f6e738ad7bd9 100644
40--- a/drivers/pwm/pwm-meson.c
41+++ b/drivers/pwm/pwm-meson.c
42@@ -111,6 +111,10 @@ struct meson_pwm {
43 const struct meson_pwm_data *data;
44 void __iomem *base;
45 u8 inverter_mask;
46+ /*
47+ * Protects register (write) access to the REG_MISC_AB register
48+ * that is shared between the two PWMs.
49+ */
50 spinlock_t lock;
51 };
52
53@@ -235,6 +239,7 @@ static void meson_pwm_enable(struct meson_pwm *meson,
54 {
55 u32 value, clk_shift, clk_enable, enable;
56 unsigned int offset;
57+ unsigned long flags;
58
59 switch (id) {
60 case 0:
61@@ -255,6 +260,8 @@ static void meson_pwm_enable(struct meson_pwm *meson,
62 return;
63 }
64
65+ spin_lock_irqsave(&meson->lock, flags);
66+
67 value = readl(meson->base + REG_MISC_AB);
68 value &= ~(MISC_CLK_DIV_MASK << clk_shift);
69 value |= channel->pre_div << clk_shift;
70@@ -267,11 +274,14 @@ static void meson_pwm_enable(struct meson_pwm *meson,
71 value = readl(meson->base + REG_MISC_AB);
72 value |= enable;
73 writel(value, meson->base + REG_MISC_AB);
74+
75+ spin_unlock_irqrestore(&meson->lock, flags);
76 }
77
78 static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id)
79 {
80 u32 value, enable;
81+ unsigned long flags;
82
83 switch (id) {
84 case 0:
85@@ -286,9 +296,13 @@ static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id)
86 return;
87 }
88
89+ spin_lock_irqsave(&meson->lock, flags);
90+
91 value = readl(meson->base + REG_MISC_AB);
92 value &= ~enable;
93 writel(value, meson->base + REG_MISC_AB);
94+
95+ spin_unlock_irqrestore(&meson->lock, flags);
96 }
97
98 static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
99@@ -296,19 +310,16 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
100 {
101 struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
102 struct meson_pwm *meson = to_meson_pwm(chip);
103- unsigned long flags;
104 int err = 0;
105
106 if (!state)
107 return -EINVAL;
108
109- spin_lock_irqsave(&meson->lock, flags);
110-
111 if (!state->enabled) {
112 meson_pwm_disable(meson, pwm->hwpwm);
113 channel->state.enabled = false;
114
115- goto unlock;
116+ return 0;
117 }
118
119 if (state->period != channel->state.period ||
120@@ -329,7 +340,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
121 err = meson_pwm_calc(meson, channel, pwm->hwpwm,
122 state->duty_cycle, state->period);
123 if (err < 0)
124- goto unlock;
125+ return err;
126
127 channel->state.polarity = state->polarity;
128 channel->state.period = state->period;
129@@ -341,9 +352,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
130 channel->state.enabled = true;
131 }
132
133-unlock:
134- spin_unlock_irqrestore(&meson->lock, flags);
135- return err;
136+ return 0;
137 }
138
139 static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
140--
1412.20.1
142